// slack-notifier CI/CD
// golang 컨테이너에서 빌드·테스트 → EC2(systemd)로 단일 정적 바이너리 배포.
//
// 필요한 Jenkins 구성 (자세한 건 deploy/README.md):
//   - 플러그인: Go Plugin, Credentials Binding
//   - Global Tool Configuration → Go 에 'go-1.26.4' 등록(이름이 tools 블록과 일치해야 함)
//   - 빌드 에이전트에 sshpass 설치 (아이디/비번 SSH 접속용)
//   - 자격증명: "Username with password" 를 'palnet-dev-ops' ID 로 등록 (유저·비번 모두 여기서 가져옴)
//   - 대상 EC2: 비밀번호 SSH 허용, 배포 계정이 APP_DIR 소유, APP_DIR 에 .env.prod 미리 배치, Caddy(HTTPS)
//   - 앱 기동은 start.sh(백그라운드, sudo/systemd 불필요)로 관리 — 배포 시 start.sh 도 함께 전송됨
//
// 호스트/유저/아키텍처는 파라미터로 조정. main 브랜치 + DEPLOY_HOST 지정 시에만 배포.

pipeline {
  agent none

  options {
    timestamps()
    disableConcurrentBuilds()
    timeout(time: 20, unit: 'MINUTES')
  }

  parameters {
    // parameters 를 바꿀 때마다 이 기본값과 environment.PARAMS_VERSION 을 함께 +1 한다.
    // (파라미터는 1빌드 늦게 등록되므로, 아래 Guard 가 stale 빌드를 막는다)
    string(name: 'PARAMS_VERSION', defaultValue: '3', description: '파라미터 동기화 가드용 — 직접 바꾸지 말 것')
    string(name: 'APP_DIR',     defaultValue: '/data/app/notifier', description: '원격 설치 경로')
    choice(name: 'ARCH',        choices: ['amd64', 'arm64'], description: 'EC2 아키텍처 (Graviton/t4g=arm64, x86=amd64)')
  }

  environment {
    DEPLOY_CRED_ID = 'palnet-dev-ops'       // Jenkins "Username with password" 자격증명 ID (EC2 접속 비번)
    DEPLOY_HOST    = '52.79.193.239'           // 'EC2 호스트(DNS 또는 IP). 비우면 배포 스킵(빌드/테스트만).')
    BIN            = 'slack-notifier'
    GOFLAGS        = '-buildvcs=false'     // .git 소유권 경고 회피
    PARAMS_VERSION = '3'                   // ← parameters 변경 시 위 기본값과 함께 +1
  }

  stages {
    // 파라미터 정의가 갱신되기 전(=stale)에 빌드/배포되는 것을 막는다.
    // Jenkinsfile 의 step/environment 는 즉시 반영되지만 parameters 는 1빌드 늦게 등록되므로,
    // "현재 코드의 PARAMS_VERSION(즉시 반영)" 과 "등록된 params.PARAMS_VERSION(지난 등록)" 이 다르면 중단.
    stage('Guard: params sync') {
      agent any
      steps {
        script {
          if (params.PARAMS_VERSION != env.PARAMS_VERSION) {
            error("파라미터가 아직 갱신되지 않음(stale): 등록=${params.PARAMS_VERSION}, 현재=${env.PARAMS_VERSION}. " +
                  "이 빌드가 파라미터를 재등록했으니, 한 번 더 빌드하면 정상 진행됩니다.")
          }
        }
      }
    }

    stage('Build & Test') {
      agent any
      // 'go-1.26' = Global Tool Configuration 의 Go 도구 이름(다르면 여기와 일치시킬 것). 컨테이너 없음 — 경량.
      tools { go 'go-1.26.4' }
      steps {
        sh 'go version'
        sh 'go vet ./...'
        sh 'go test ./...'
        sh "CGO_ENABLED=0 GOOS=linux GOARCH=${params.ARCH} go build -ldflags='-s -w' -o ${BIN} ."
        sh "ls -lh ${BIN}"
        stash name: 'binary', includes: "${BIN}"
      }
    }

    stage('Deploy') {
      when {
        allOf {
          branch 'main'
          expression { return env.DEPLOY_HOST?.trim() }
        }
      }
      agent any
      steps {
        unstash 'binary'
        // 아이디/비번 SSH (sshpass). 유저(CRED_USER)·비번(PASSWORD) 모두 palnet-dev-ops 자격증명에서.
        // sshpass -e 로 비번을 SSHPASS 환경변수로 전달 → 커맨드라인/ps 에 노출 안 됨.
        withCredentials([usernamePassword(credentialsId: env.DEPLOY_CRED_ID,
                         usernameVariable: 'CRED_USER', passwordVariable: 'PASSWORD')]) {
          sh '''
            set -eu
            export SSHPASS="$PASSWORD"
            H="${CRED_USER}@${DEPLOY_HOST}"   # 유저·비번 모두 palnet-dev-ops 자격증명에서
            # scp 는 포트 옵션이 대문자 -P, ssh 는 소문자 -p
            SCP="sshpass -e scp -P 22 -o StrictHostKeyChecking=no"
            SSH="sshpass -e ssh -p 22 -o StrictHostKeyChecking=no"

            # 0) 배포 디렉터리 보장
            $SSH "$H" "mkdir -p '${APP_DIR}'"

            # 1) 바이너리(임시명)와 start.sh 전송
            $SCP "${BIN}"        "$H:${APP_DIR}/${BIN}.new"
            $SCP deploy/start.sh "$H:${APP_DIR}/start.sh"

            # 2) 프로세스 정지 후 바이너리 교체(정지 상태라 ETXTBSY 없음) → start.sh 로 재기동.
            #    sudo/systemd 불필요 — 배포 계정이 APP_DIR 소유. .env.prod 는 서버에 미리 두어야 함.
            $SSH "$H" "set -e; cd '${APP_DIR}'; \
              chmod +x start.sh; \
              ./start.sh stop || true; \
              mv -f '${BIN}.new' '${BIN}'; \
              chmod 0755 '${BIN}'; \
              ./start.sh start prod; \
              ./start.sh status"
          '''
        }
      }
    }
  }

  post {
    success { echo "✅ ${env.BRANCH_NAME} 빌드 성공" }
    failure { echo "❌ 빌드 실패 — 콘솔 로그 확인" }
  }
}
