diff --git a/.gitignore b/.gitignore index 1e40553..018edbd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ -# Секреты и ключи +# Секреты и ключи *.pem *.key + +# Workspace bootstrap — локальные секреты (только на машине) +scripts/workspace-bootstrap/config/workspace.local.env +**/workspace.local.env diff --git a/docs/git.md b/docs/git.md index e255aa2..8ba5113 100644 --- a/docs/git.md +++ b/docs/git.md @@ -1,4 +1,4 @@ -# Работа с Git +# Работа с Git Три **SSH**-remote (без HTTPS и без `origin`): @@ -25,12 +25,16 @@ git clone ssh://git@git.kalinamall.lan:2222/PapaTramp/reverse-proxy.git cd reverse-proxy ``` -На Windows после [Setup-SshAccess](https://git.kalinamall.ru/PapaTramp/Answer.and.other.shit) (или локально): +На Windows: ```powershell -Set-GitRemotes.ps1 -Repo reverse-proxy +cd scripts\workspace-bootstrap +.\init-machine.ps1 +.\scripts\Set-GitRemotes.ps1 -Repo reverse-proxy ``` +Полная настройка новой машины: [scripts/workspace-bootstrap/README.md](../scripts/workspace-bootstrap/README.md). + ## Обновление локальной копии ```bash diff --git a/docs/repo-policy.md b/docs/repo-policy.md index 6395274..ee3ae1d 100644 --- a/docs/repo-policy.md +++ b/docs/repo-policy.md @@ -1,4 +1,4 @@ -# Политика репозиториев: git.kalinamall.ru и GitHub +# Политика репозиториев: git.kalinamall.ru и GitHub Для перечисленных проектов действует единая схема: **основное хранилище — корпоративный Gitea**, **GitHub — резервная копия без секретов**. @@ -37,13 +37,31 @@ ## Настройка remotes (один раз на машине) -Только **SSH**, три имени (скрипт `Set-GitRemotes.ps1` из Answer.and.other.shit): +Только **SSH**, три имени — скрипт **`scripts/workspace-bootstrap`** (Mac/Linux: `Set-GitRemotes.sh`, Windows: `Set-GitRemotes.ps1`): + +```bash +cd ~/Documents/Cursor/Projects/reverse-proxy/scripts/workspace-bootstrap +cp config/workspace.local.env.example config/workspace.local.env +# заполнить workspace.local.env +./init-machine.sh +``` + +Или для одного репо: + +```bash +./scripts/Set-GitRemotes.sh reverse-proxy +``` + +Windows: ```powershell -cd C:\Users\papat\Projects\reverse-proxy -..\Answer.and.other.shit\scripts\ssh-setup\Set-GitRemotes.ps1 -Repo reverse-proxy +cd C:\Users\papat\Projects\reverse-proxy\scripts\workspace-bootstrap +.\init-machine.ps1 +..\scripts\Set-GitRemotes.ps1 -Repo reverse-proxy ``` +Старый вариант (Answer.and.other.shit) — переносится в `scripts/workspace-bootstrap`. + Или вручную: ```bash diff --git a/scripts/workspace-bootstrap/README.md b/scripts/workspace-bootstrap/README.md new file mode 100644 index 0000000..763ac53 --- /dev/null +++ b/scripts/workspace-bootstrap/README.md @@ -0,0 +1,67 @@ +# Workspace bootstrap + +Единая настройка **Mac / Windows** для Git (SSH), Cursor и политики push. + +Целевое место в Gitea: репозиторий **Answer.and.other.shit** (можно скопировать эту папку в `scripts/workspace-bootstrap/`). + +## Быстрый старт (новая машина) + +```bash +# 1. Клонировать bootstrap (или весь Answer.and.other.shit) +cd scripts/workspace-bootstrap + +# 2. Секреты и адреса — один файл (не в git!) +cp config/workspace.local.env.example config/workspace.local.env +# отредактировать workspace.local.env + +# 3. Машина: SSH, git, клоны, remotes +./init-machine.sh + +# 4. Cursor: правила в проекты +./init-cursor.sh + +# 5. Добавить ~/.ssh/id_ed25519_kalinamall.pub в Gitea (kalinamall + home) и GitHub +./init-machine.sh --verify +``` + +Windows (PowerShell): + +```powershell +cd scripts\workspace-bootstrap +copy config\workspace.local.env.example config\workspace.local.env +# отредактировать +.\init-machine.ps1 +.\init-cursor.sh # через Git Bash, или: bash init-cursor.sh +.\init-machine.ps1 -Verify +``` + +## Файлы + +| Файл | В git | Назначение | +|------|-------|------------| +| `config/workspace.local.env.example` | да | шаблон | +| `config/workspace.local.env` | **нет** | логины, IP, пароли, пути | +| `config/repos.yaml` | да | список репозиториев | +| `config/ssh-config.template` | да | `~/.ssh/config` | +| `config/hosts.template` | да | строки для `/etc/hosts` или `C:\Windows\System32\drivers\etc\hosts` | + +## Push + +```bash +cd ~/Documents/Cursor/Projects/reverse-proxy +../../reverse-proxy/scripts/workspace-bootstrap/push-safe.sh +# или из bootstrap: ./push-safe.sh /path/to/repo +``` + +- **kalinamall** — всегда (с секретами) +- **home** — если `PUSH_HOME=yes` в env +- **github** — только после `check-no-secrets.sh` + +## Переменная окружения + +```bash +export WORKSPACE_ENV=/path/to/workspace.local.env +./init-machine.sh +``` + +По умолчанию: `config/workspace.local.env` рядом со скриптами. diff --git a/scripts/workspace-bootstrap/config/hosts.template b/scripts/workspace-bootstrap/config/hosts.template new file mode 100644 index 0000000..f1146b8 --- /dev/null +++ b/scripts/workspace-bootstrap/config/hosts.template @@ -0,0 +1,3 @@ +# Строки для hosts (подставляются IP из workspace.local.env) +__GITEA_KALINAMALL_IP__ __GITEA_KALINAMALL_HOST__ __GITEA_KALINAMALL_HOST_PUBLIC__ +__GITEA_KALINAMALL_IP__ sac.kalinamall.ru sac-api.kalinamall.ru diff --git a/scripts/workspace-bootstrap/config/repos.yaml b/scripts/workspace-bootstrap/config/repos.yaml new file mode 100644 index 0000000..638cb3d --- /dev/null +++ b/scripts/workspace-bootstrap/config/repos.yaml @@ -0,0 +1,22 @@ +# Список репозиториев PapaTramp / PTah +# github: false — не добавлять remote github, не пушить на GitHub + +repos: + - name: reverse-proxy + github: true + + - name: security-alert-center + github: true + + - name: seaca + github: false + + - name: rdp-login-monitor + github: true + github_name: RDP-login-monitor + + - name: ssh-monitor + github: true + + - name: Answer.and.other.shit + github: false diff --git a/scripts/workspace-bootstrap/config/ssh-config.template b/scripts/workspace-bootstrap/config/ssh-config.template new file mode 100644 index 0000000..d7fc8c3 --- /dev/null +++ b/scripts/workspace-bootstrap/config/ssh-config.template @@ -0,0 +1,28 @@ +# Сгенерировано init-machine.sh — ручные правки могут быть перезаписаны +# Блок BEGIN WORKSPACE-BOOTSTRAP … END + +Host github.com + User git + IdentityFile __SSH_KEY_PATH__ + IdentitiesOnly yes + +Host __GITEA_KALINAMALL_HOST__ __GITEA_KALINAMALL_HOST_PUBLIC__ + User git + HostName __GITEA_KALINAMALL_HOST__ + Port __GITEA_KALINAMALL_SSH_PORT__ + IdentityFile __SSH_KEY_PATH__ + IdentitiesOnly yes + +Host __GITEA_HOME_HOST__ __GITEA_HOME_HOST_PUBLIC__ + User git + HostName __GITEA_HOME_HOST__ + Port __GITEA_HOME_SSH_PORT__ + IdentityFile __SSH_KEY_PATH__ + IdentitiesOnly yes + +Host haproxy haproxy.kalinamall.ru __SSH_HAPROXY_IP__ + User __SSH_HAPROXY_USER__ + IdentityFile __SSH_KEY_PATH__ + IdentitiesOnly yes + +# END WORKSPACE-BOOTSTRAP diff --git a/scripts/workspace-bootstrap/config/workspace.local.env.example b/scripts/workspace-bootstrap/config/workspace.local.env.example new file mode 100644 index 0000000..724fa85 --- /dev/null +++ b/scripts/workspace-bootstrap/config/workspace.local.env.example @@ -0,0 +1,57 @@ +# Копия: config/workspace.local.env (не коммитить!) +# Все адреса, логины и пароли для init-скриптов — только в workspace.local.env + +# --- Профиль машины: home-mac | home-win | work-win --- +MACHINE_PROFILE="home-mac" + +# --- Git (identity) --- +GIT_USER_NAME="Your Name" +GIT_USER_EMAIL="you@example.com" + +# --- SSH-ключ для Git (создаётся init-machine, если файла нет) --- +SSH_KEY_BASENAME="id_ed25519_kalinamall" +SSH_KEY_COMMENT="papatramp" + +# --- Корпоративный Gitea (remote: kalinamall) --- +GITEA_KALINAMALL_HOST="git.kalinamall.lan" +GITEA_KALINAMALL_HOST_PUBLIC="git.kalinamall.ru" +GITEA_KALINAMALL_IP="192.168.160.129" +GITEA_KALINAMALL_SSH_PORT="2222" +GITEA_KALINAMALL_ORG="PapaTramp" + +# --- Домашний Gitea (remote: home) --- +GITEA_HOME_HOST="git.papatramp.lan" +GITEA_HOME_HOST_PUBLIC="git.papatramp.ru" +GITEA_HOME_SSH_PORT="2222" +GITEA_HOME_ORG="PapaTramp" + +# --- GitHub (remote: github) --- +GITHUB_USER="PTah" + +# --- Каталог проектов --- +PROJECTS_DIR_MAC="${HOME}/Documents/Cursor/Projects" +PROJECTS_DIR_WIN="C:/Users/papat/Projects" + +# --- Push --- +PUSH_HOME="yes" +PUSH_GITHUB="yes" + +# --- /etc/hosts (опционально: yes — init добавит строки из hosts.template) --- +UPDATE_HOSTS="yes" + +# --- SSH на серверы (для справки / будущих скриптов; не для git remote) --- +SSH_HAPROXY_IP="192.168.160.117" +SSH_HAPROXY_USER="papatramp" +SSH_HAPROXY_PASSWORD="" + +SSH_GIT_SERVER_IP="192.168.160.129" +SSH_GIT_SERVER_USER="papatramp" +SSH_GIT_SERVER_PASSWORD="" + +SSH_SAC_SERVER_IP="192.168.160.145" +SSH_SAC_SERVER_USER="papatramp" +SSH_SAC_SERVER_PASSWORD="" + +# --- Админ / VPN (справочно) --- +ADMIN_PUBLIC_IP="5.100.81.121" +VPN_CIDR="192.168.128.0/17" diff --git a/scripts/workspace-bootstrap/cursor/rules/repo-policy.mdc b/scripts/workspace-bootstrap/cursor/rules/repo-policy.mdc new file mode 100644 index 0000000..cfeca78 --- /dev/null +++ b/scripts/workspace-bootstrap/cursor/rules/repo-policy.mdc @@ -0,0 +1,18 @@ +--- +description: Политика remotes и push — kalinamall/home/github +alwaysApply: true +--- + +# Git remotes (kalinamall.ru) + +- **kalinamall** — основной Gitea, полный код и секреты. `git push kalinamall main`. +- **home** — домашний Gitea, полная копия. +- **github** — только резерв **без секретов**. Перед push: `check-no-secrets.sh`. +- Remotes только **SSH** (`ssh://`, `git@`). HTTPS и `origin` не использовать. +- Не коммитить в GitHub: `.env`, ключи, пароли, токены, `google-services.json`, prod credentials. +- См. `docs/repo-policy.md` в reverse-proxy. + +# Cursor + +- Отвечать на русском, если пользователь пишет по-русски. +- Коммиты только по явной просьбе пользователя. diff --git a/scripts/workspace-bootstrap/hooks/pre-push b/scripts/workspace-bootstrap/hooks/pre-push new file mode 100755 index 0000000..03f5c22 --- /dev/null +++ b/scripts/workspace-bootstrap/hooks/pre-push @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Git pre-push: проверка секретов только для remote github +set -Eeuo pipefail + +remote="${1:-}" +url="${2:-}" + +[[ $remote == github ]] || exit 0 + +# Найти bootstrap от корня репо +repo_root=$(git rev-parse --show-toplevel) +checker="" + +for candidate in \ + "$repo_root/scripts/workspace-bootstrap/scripts/check-no-secrets.sh" \ + "$repo_root/../reverse-proxy/scripts/workspace-bootstrap/scripts/check-no-secrets.sh" \ + "$HOME/Documents/Cursor/Projects/reverse-proxy/scripts/workspace-bootstrap/scripts/check-no-secrets.sh"; do + if [[ -f $candidate ]]; then + checker=$candidate + break + fi +done + +if [[ -z $checker ]]; then + echo "pre-push: check-no-secrets.sh не найден — пропуск проверки" + exit 0 +fi + +exec bash "$checker" github diff --git a/scripts/workspace-bootstrap/init-cursor.sh b/scripts/workspace-bootstrap/init-cursor.sh new file mode 100755 index 0000000..01f41ea --- /dev/null +++ b/scripts/workspace-bootstrap/init-cursor.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# shellcheck source=lib/common.sh +source "$ROOT/lib/common.sh" + +load_workspace_env + +PROJ=$(projects_dir) +RULES_SRC="$ROOT/cursor/rules" + +[[ -d $RULES_SRC ]] || die "нет $RULES_SRC" + +while read -r repo _gh; do + [[ -z $repo ]] && continue + dest="$PROJ/$repo/.cursor/rules" + if [[ -d $PROJ/$repo ]]; then + mkdir -p "$dest" + cp -f "$RULES_SRC"/*.mdc "$dest/" 2>/dev/null || true + log "cursor rules → $dest" + fi +done < <(each_repo) + +# User rules (опционально — копия в ~/.cursor/rules если каталог есть) +USER_RULES="$HOME/.cursor/rules" +if [[ -d $(dirname "$USER_RULES") ]]; then + mkdir -p "$USER_RULES" + cp -f "$RULES_SRC"/*.mdc "$USER_RULES/" 2>/dev/null || true + log "cursor user rules → $USER_RULES" +fi + +log "init-cursor готово" diff --git a/scripts/workspace-bootstrap/init-machine.ps1 b/scripts/workspace-bootstrap/init-machine.ps1 new file mode 100644 index 0000000..deef58a --- /dev/null +++ b/scripts/workspace-bootstrap/init-machine.ps1 @@ -0,0 +1,42 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Инициализация машины: SSH, git, клоны (Windows). +.DESCRIPTION + Вызывает init-machine.sh через Git Bash, если доступен. +#> +param( + [switch]$Verify +) + +$ErrorActionPreference = 'Stop' +$BootstrapRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +$ShScript = Join-Path $BootstrapRoot 'init-machine.sh' + +$envExample = Join-Path $BootstrapRoot 'config\workspace.local.env.example' +$envLocal = Join-Path $BootstrapRoot 'config\workspace.local.env' +if (-not (Test-Path $envLocal)) { + Write-Host "Создайте config\workspace.local.env из workspace.local.env.example" -ForegroundColor Yellow + if (Test-Path $envExample) { + Copy-Item $envExample $envLocal + Write-Host "Скопирован шаблон → workspace.local.env — заполните и запустите снова." + } + exit 1 +} + +$gitBash = @( + "${env:ProgramFiles}\Git\bin\bash.exe", + "${env:ProgramFiles(x86)}\Git\bin\bash.exe" +) | Where-Object { Test-Path $_ } | Select-Object -First 1 + +$args = @() +if ($Verify) { $args += '--verify' } + +if ($gitBash) { + & $gitBash $ShScript @args + exit $LASTEXITCODE +} + +Write-Host "Git Bash не найден. Установите Git for Windows или выполните вручную:" -ForegroundColor Red +Write-Host " bash init-machine.sh $($args -join ' ')" +exit 1 diff --git a/scripts/workspace-bootstrap/init-machine.sh b/scripts/workspace-bootstrap/init-machine.sh new file mode 100755 index 0000000..29ad939 --- /dev/null +++ b/scripts/workspace-bootstrap/init-machine.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# shellcheck source=lib/common.sh +source "$ROOT/lib/common.sh" + +VERIFY=0 +while [[ $# -gt 0 ]]; do + case "$1" in + --verify) VERIFY=1; shift ;; + -h | --help) + echo "Usage: init-machine.sh [--verify]" + exit 0 + ;; + *) die "unknown option: $1" ;; + esac +done + +load_workspace_env + +log "профиль: ${MACHINE_PROFILE:-?}, OS: $(detect_os)" + +# --- SSH key --- +KEY_PATH=$(ssh_key_path) +mkdir -p "$(ssh_home)" +if [[ ! -f $KEY_PATH ]]; then + log "создаю ключ $KEY_PATH" + ssh-keygen -t ed25519 -C "${SSH_KEY_COMMENT:-papatramp}@$(hostname -s 2>/dev/null || hostname)" -f "$KEY_PATH" -N "" + log "добавьте публичный ключ в Gitea (kalinamall + home) и GitHub:" + echo "----------" + cat "${KEY_PATH}.pub" + echo "----------" +else + log "ключ уже есть: $KEY_PATH" +fi + +# --- ssh config --- +TMP_BLOCK=$(mktemp) +render_template "$ROOT/config/ssh-config.template" "$TMP_BLOCK" +merge_ssh_config "$TMP_BLOCK" +rm -f "$TMP_BLOCK" +log "обновлён $(ssh_home)/config" + +# --- known_hosts --- +for target in "${GITEA_KALINAMALL_HOST}:${GITEA_KALINAMALL_SSH_PORT}" \ + "${GITEA_HOME_HOST}:${GITEA_HOME_SSH_PORT}" "github.com"; do + host=${target%%:*} + port=${target##*:} + if [[ $host == $port ]]; then port=22; fi + log "ssh-keyscan $host:$port" + ssh-keyscan -p "$port" "$host" 2>/dev/null >>"$(ssh_home)/known_hosts" || true +done + +# --- git global: только SSH, без credential prompts --- +if [[ -n ${GIT_USER_NAME:-} ]]; then + git config --global user.name "$GIT_USER_NAME" +fi +if [[ -n ${GIT_USER_EMAIL:-} ]]; then + git config --global user.email "$GIT_USER_EMAIL" +fi +git config --global credential.helper "" +git config --global url."git@github.com:".insteadOf "https://github.com/" +git config --global url."ssh://git@${GITEA_KALINAMALL_HOST}:${GITEA_KALINAMALL_SSH_PORT}/".insteadOf "https://${GITEA_KALINAMALL_HOST_PUBLIC}/" +git config --global url."ssh://git@${GITEA_HOME_HOST}:${GITEA_HOME_SSH_PORT}/".insteadOf "https://${GITEA_HOME_HOST_PUBLIC}/" +log "git global: SSH-only, credential.helper отключён" + +# --- hosts (optional) --- +if [[ ${UPDATE_HOSTS:-no} == yes ]]; then + HOSTS_TMP=$(mktemp) + sed \ + -e "s|__GITEA_KALINAMALL_IP__|${GITEA_KALINAMALL_IP}|g" \ + -e "s|__GITEA_KALINAMALL_HOST__|${GITEA_KALINAMALL_HOST}|g" \ + -e "s|__GITEA_KALINAMALL_HOST_PUBLIC__|${GITEA_KALINAMALL_HOST_PUBLIC}|g" \ + "$ROOT/config/hosts.template" >"$HOSTS_TMP" + if [[ $(detect_os) == mac ]]; then + log "добавление в /etc/hosts (нужен sudo)" + while read -r line; do + [[ -z $line || $line == \#* ]] && continue + grep -qF "$line" /etc/hosts 2>/dev/null || echo "$line" | sudo tee -a /etc/hosts >/dev/null + done <"$HOSTS_TMP" + elif [[ $(detect_os) == win ]]; then + log "Windows: добавьте вручную в C:\\Windows\\System32\\drivers\\etc\\hosts:" + cat "$HOSTS_TMP" + fi + rm -f "$HOSTS_TMP" +fi + +# --- clone + remotes --- +PROJ=$(projects_dir) +mkdir -p "$PROJ" +while read -r repo has_gh; do + [[ -z $repo ]] && continue + dest="$PROJ/$repo" + if [[ ! -d $dest/.git ]]; then + log "клонирование $repo" + git clone "$(kalinamall_remote_url "$repo")" "$dest" || log "клон не удался (сеть/ключ?) — настройте ключ и повторите" + fi + if [[ -d $dest/.git ]]; then + bash "$ROOT/scripts/Set-GitRemotes.sh" --repo-dir "$dest" "$repo" "$has_gh" + # pre-push hook для github + if [[ $has_gh == 1 && -f $ROOT/hooks/pre-push ]]; then + install -m 755 "$ROOT/hooks/pre-push" "$dest/.git/hooks/pre-push" + fi + fi +done < <(each_repo) + +if [[ $VERIFY == 1 ]]; then + log "проверка SSH (BatchMode — без запроса пароля)" + ssh -o BatchMode=yes -o ConnectTimeout=8 -p "$GITEA_KALINAMALL_SSH_PORT" -T "git@${GITEA_KALINAMALL_HOST}" 2>&1 | head -3 || true + ssh -o BatchMode=yes -o ConnectTimeout=8 -p "$GITEA_HOME_SSH_PORT" -T "git@${GITEA_HOME_HOST}" 2>&1 | head -3 || true + ssh -o BatchMode=yes -o ConnectTimeout=8 -T git@github.com 2>&1 | head -3 || true + log "если выше «Permission denied» — добавьте .pub в Gitea/GitHub и повторите --verify" +fi + +log "готово" diff --git a/scripts/workspace-bootstrap/lib/common.sh b/scripts/workspace-bootstrap/lib/common.sh new file mode 100644 index 0000000..c572ace --- /dev/null +++ b/scripts/workspace-bootstrap/lib/common.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# Общие функции workspace-bootstrap +set -Eeuo pipefail + +BOOTSTRAP_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +die() { echo "workspace-bootstrap: error: $*" >&2; exit 1; } +log() { echo "[bootstrap] $*"; } + +load_workspace_env() { + local env_file="${WORKSPACE_ENV:-$BOOTSTRAP_ROOT/config/workspace.local.env}" + [[ -f $env_file ]] || die "нет $env_file — скопируйте config/workspace.local.env.example и заполните" + # shellcheck source=/dev/null + set -a + source "$env_file" + set +a +} + +detect_os() { + case "$(uname -s)" in + Darwin) echo mac ;; + MINGW* | MSYS* | CYGWIN*) echo win ;; + *) echo linux ;; + esac +} + +ssh_home() { + if [[ $(detect_os) == win ]]; then + if [[ -n ${HOME:-} ]]; then printf '%s/.ssh' "$HOME" + else printf '%s/.ssh' "$USERPROFILE" + fi + else + printf '%s/.ssh' "$HOME" + fi +} + +projects_dir() { + local os + os=$(detect_os) + if [[ $os == mac ]]; then + # shellcheck disable=SC2086 + eval echo "${PROJECTS_DIR_MAC:-$HOME/Documents/Cursor/Projects}" + elif [[ $os == win ]]; then + echo "${PROJECTS_DIR_WIN:-$HOME/Projects}" + else + eval echo "${PROJECTS_DIR_MAC:-$HOME/projects}" + fi +} + +ssh_key_path() { + printf '%s/%s' "$(ssh_home)" "${SSH_KEY_BASENAME:-id_ed25519_kalinamall}" +} + +expand_path() { + local p=$1 + if [[ $p == ~* ]]; then + p="${p/#\~/$HOME}" + fi + printf '%s' "$p" +} + +render_template() { + local template=$1 dest=$2 + local key_path + key_path=$(ssh_key_path) + [[ -f $key_path ]] || key_path="${key_path}.example" + sed \ + -e "s|__SSH_KEY_PATH__|$key_path|g" \ + -e "s|__GITEA_KALINAMALL_HOST__|${GITEA_KALINAMALL_HOST}|g" \ + -e "s|__GITEA_KALINAMALL_HOST_PUBLIC__|${GITEA_KALINAMALL_HOST_PUBLIC}|g" \ + -e "s|__GITEA_KALINAMALL_SSH_PORT__|${GITEA_KALINAMALL_SSH_PORT}|g" \ + -e "s|__GITEA_HOME_HOST__|${GITEA_HOME_HOST}|g" \ + -e "s|__GITEA_HOME_HOST_PUBLIC__|${GITEA_HOME_HOST_PUBLIC}|g" \ + -e "s|__GITEA_HOME_SSH_PORT__|${GITEA_HOME_SSH_PORT}|g" \ + -e "s|__SSH_HAPROXY_IP__|${SSH_HAPROXY_IP}|g" \ + -e "s|__SSH_HAPROXY_USER__|${SSH_HAPROXY_USER}|g" \ + "$template" >"$dest" +} + +merge_ssh_config() { + local block_file=$1 + local ssh_config + ssh_config="$(ssh_home)/config" + mkdir -p "$(ssh_home)" + touch "$ssh_config" + if grep -q 'BEGIN WORKSPACE-BOOTSTRAP' "$ssh_config" 2>/dev/null; then + local tmp + tmp=$(mktemp) + awk '/# BEGIN WORKSPACE-BOOTSTRAP/{skip=1} !skip{print} /# END WORKSPACE-BOOTSTRAP/{skip=0}' "$ssh_config" >"$tmp" + mv "$tmp" "$ssh_config" + fi + { + echo "" + echo "# BEGIN WORKSPACE-BOOTSTRAP" + cat "$block_file" + echo "# END WORKSPACE-BOOTSTRAP" + } >>"$ssh_config" + chmod 600 "$ssh_config" 2>/dev/null || true +} + +each_repo() { + local repos_file="$BOOTSTRAP_ROOT/config/repos.yaml" + [[ -f $repos_file ]] || die "missing $repos_file" + awk ' + /^ - name:/ { if (n != "") print n, gh; n = $3; gh = 0 } + /github: true/ { gh = 1 } + /github: false/ { gh = 0 } + END { if (n != "") print n, gh } + ' "$repos_file" +} + +github_repo_name() { + local name=$1 + local repos_file="$BOOTSTRAP_ROOT/config/repos.yaml" + local custom + custom=$(awk -v repo="$name" ' + $0 ~ "- name: " repo { found=1; next } + found && /github_name:/ { print $2; exit } + ' "$repos_file") + if [[ -n $custom ]]; then + echo "$custom" + else + echo "$name" + fi +} + +kalinamall_remote_url() { + local repo=$1 + printf 'ssh://git@%s:%s/%s/%s.git' \ + "$GITEA_KALINAMALL_HOST" "$GITEA_KALINAMALL_SSH_PORT" "$GITEA_KALINAMALL_ORG" "$repo" +} + +home_remote_url() { + local repo=$1 + printf 'ssh://git@%s:%s/%s/%s.git' \ + "$GITEA_HOME_HOST" "$GITEA_HOME_SSH_PORT" "$GITEA_HOME_ORG" "$repo" +} + +github_remote_url() { + local repo=$1 + local gh_name + gh_name=$(github_repo_name "$repo") + printf 'git@%s:%s/%s.git' 'github.com' "$GITHUB_USER" "$gh_name" +} diff --git a/scripts/workspace-bootstrap/push-safe.sh b/scripts/workspace-bootstrap/push-safe.sh new file mode 100755 index 0000000..9864517 --- /dev/null +++ b/scripts/workspace-bootstrap/push-safe.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# shellcheck source=lib/common.sh +source "$ROOT/lib/common.sh" + +REPO_DIR="${1:-.}" +[[ -d $REPO_DIR ]] || die "каталог не найден: $REPO_DIR" +cd "$REPO_DIR" +[[ -d .git ]] || die "не git: $REPO_DIR" + +load_workspace_env + +BRANCH=$(git rev-parse --abbrev-ref HEAD) +log "push-safe в $(pwd) ветка $BRANCH" + +git push kalinamall "$BRANCH" || die "push kalinamall failed" + +if [[ ${PUSH_HOME:-yes} == yes ]]; then + git push home "$BRANCH" || log "push home пропущен (сеть?)" +fi + +if [[ ${PUSH_GITHUB:-yes} == yes ]] && git remote | grep -qx github; then + bash "$ROOT/scripts/check-no-secrets.sh" || exit 1 + git push github "$BRANCH" || die "push github failed" + log "github OK" +else + log "github remote нет или PUSH_GITHUB=no" +fi + +log "готово" diff --git a/scripts/workspace-bootstrap/scripts/Set-GitRemotes.ps1 b/scripts/workspace-bootstrap/scripts/Set-GitRemotes.ps1 new file mode 100644 index 0000000..d604df0 --- /dev/null +++ b/scripts/workspace-bootstrap/scripts/Set-GitRemotes.ps1 @@ -0,0 +1,28 @@ +#Requires -Version 5.1 +param( + [Parameter(Mandatory = $true)] + [string]$Repo, + + [string]$RepoDir = '' +) + +$ErrorActionPreference = 'Stop' +$ScriptsDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$BootstrapRoot = Split-Path -Parent $ScriptsDir +$ShScript = Join-Path $ScriptsDir 'Set-GitRemotes.sh' + +$gitBash = @( + "${env:ProgramFiles}\Git\bin\bash.exe", + "${env:ProgramFiles(x86)}\Git\bin\bash.exe" +) | Where-Object { Test-Path $_ } | Select-Object -First 1 + +$bashArgs = @($ShScript, $Repo) +if ($RepoDir) { $bashArgs = @($ShScript, '--repo-dir', $RepoDir, $Repo) } + +if ($gitBash) { + & $gitBash @bashArgs + exit $LASTEXITCODE +} + +Write-Host "Git Bash не найден." +exit 1 diff --git a/scripts/workspace-bootstrap/scripts/Set-GitRemotes.sh b/scripts/workspace-bootstrap/scripts/Set-GitRemotes.sh new file mode 100755 index 0000000..c47feba --- /dev/null +++ b/scripts/workspace-bootstrap/scripts/Set-GitRemotes.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# Настроить remotes для одного репозитория (только SSH) +set -Eeuo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=../lib/common.sh +source "$SCRIPT_DIR/../lib/common.sh" + +usage() { + cat <<'EOF' +Usage: Set-GitRemotes.sh [--repo-dir PATH] REPO_NAME [HAS_GITHUB] + + REPO_NAME каталог проекта (имя в repos.yaml) + HAS_GITHUB 1 или 0 (по умолчанию из repos.yaml) + +Remotes: kalinamall, home, github (если включён). Без origin и без https. +EOF +} + +REPO_NAME="" +REPO_DIR="" +HAS_GITHUB="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --repo-dir) REPO_DIR=$2; shift 2 ;; + -h | --help) usage; exit 0 ;; + *) + if [[ -z $REPO_NAME ]]; then REPO_NAME=$1 + else HAS_GITHUB=$1 + fi + shift + ;; + esac +done + +[[ -n $REPO_NAME ]] || { usage; exit 1; } + +load_workspace_env + +if [[ -z $REPO_DIR ]]; then + REPO_DIR="$(projects_dir)/$REPO_NAME" +fi + +[[ -d $REPO_DIR/.git ]] || die "не git-репозиторий: $REPO_DIR" + +if [[ -z $HAS_GITHUB ]]; then + HAS_GITHUB=$(each_repo | awk -v n="$REPO_NAME" '$1==n{print $2; exit}') + [[ -n $HAS_GITHUB ]] || die "репозиторий $REPO_NAME не найден в repos.yaml" +fi + +cd "$REPO_DIR" + +set_remote() { + local name=$1 url=$2 + if git remote | grep -qx "$name"; then + git remote set-url "$name" "$url" + else + git remote add "$name" "$url" + fi +} + +# Убрать origin/https если были +if git remote | grep -qx origin; then + local_url=$(git remote get-url origin 2>/dev/null || true) + if [[ $local_url == https://* ]]; then + log "удаляю origin с HTTPS: $local_url" + git remote remove origin + fi +fi + +set_remote kalinamall "$(kalinamall_remote_url "$REPO_NAME")" +set_remote home "$(home_remote_url "$REPO_NAME")" + +if [[ $HAS_GITHUB == 1 ]]; then + set_remote github "$(github_remote_url "$REPO_NAME")" +elif git remote | grep -qx github; then + log "удаляю github remote для $REPO_NAME (github: false)" + git remote remove github +fi + +git branch -u kalinamall/main main 2>/dev/null || git branch -u kalinamall/main master 2>/dev/null || true + +log "$REPO_NAME remotes:" +git remote -v diff --git a/scripts/workspace-bootstrap/scripts/check-no-secrets.sh b/scripts/workspace-bootstrap/scripts/check-no-secrets.sh new file mode 100755 index 0000000..9c73736 --- /dev/null +++ b/scripts/workspace-bootstrap/scripts/check-no-secrets.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# Блокирует push на github при подозрении на секреты в коммитах +set -Eeuo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# shellcheck source=lib/common.sh +source "$ROOT/lib/common.sh" + +PATTERNS=( + '\.env$' + 'SECRET_KEY' + 'DATABASE_URL=' + 'password\s*=\s*["\x27][^"\x27]+' + 'BEGIN (RSA |OPENSSH )?PRIVATE KEY' + 'api[_-]?key' + 'telegram.*token' + 'smtp.*password' + 'credentials\.json' + 'google-services\.json' + '\.pem$' + '\.p12$' + '\.jks$' +) + +scan_range() { + local range=$1 + local hits=0 + local files + files=$(git diff --name-only $range 2>/dev/null; git diff --cached --name-only 2>/dev/null) + files=$(echo "$files" | sort -u | grep -v '^$' || true) + while read -r f; do + [[ -z $f ]] && continue + [[ -f $f ]] || continue + case $f in + *.example | *.sample | *env.example | workspace.local.env.example) continue ;; + esac + for pat in "${PATTERNS[@]}"; do + if grep -qiE "$pat" "$f" 2>/dev/null; then + echo " SECRET? $f (pattern: $pat)" + hits=$((hits + 1)) + fi + done + done <<<"$files" + return $hits +} + +main() { + local remote="${1:-github}" + local branch + branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo main) + + if ! git remote | grep -qx "$remote"; then + exit 0 + fi + + local range="" + if git rev-parse "$remote/$branch" >/dev/null 2>&1; then + range="$remote/$branch..HEAD" + else + range="HEAD" + fi + + echo "[check-no-secrets] сканирование перед push $remote ($range)" + local n=0 + scan_range "$range" || n=$? + + if [[ $n -gt 0 ]]; then + echo "[check-no-secrets] найдено подозрительных совпадений: $n" + echo "push на $remote отменён. Проверьте diff или используйте только kalinamall." + exit 1 + fi + echo "[check-no-secrets] OK" +} + +main github