From d7e044cf5c90e66ff67685a240381336893b3450 Mon Sep 17 00:00:00 2001 From: toki Date: Sat, 30 May 2026 19:10:05 +0900 Subject: [PATCH] update: project documentation and build scripts - Add smoke-compose script for compose validation - Update build, lint, test scripts - Update agent roadmap and phase documentation - Update project README and docs --- README.md | 4 + agent-roadmap/current.md | 2 +- agent-roadmap/phase/upstream-runtime/PHASE.md | 8 +- .../milestones/cicd-operations.md | 39 +++--- bin/build | 12 ++ bin/lint | 12 ++ bin/smoke-compose | 111 ++++++++++++++++++ bin/test | 63 ++++++++++ docs/README.md | 3 +- services/core/compose/README.md | 5 +- 10 files changed, 230 insertions(+), 29 deletions(-) create mode 100755 bin/smoke-compose diff --git a/README.md b/README.md index 2489cbc1..6c97e205 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ docker compose up | `flutter-test` 호스트 직접 실행 | `cd apps/flutter-test && flutter run` | Flutter SDK가 필요하다. | | 플러그인 직접 테스트 | `cd packages/messaging_flutter && flutter test` | Flutter SDK가 필요하다. | | Android 네이티브 단위 테스트 실행 | `cd apps/flutter-test/android && ./gradlew testDebugUnitTest` | Android SDK가 필요하다. | +| Flutter SDK integration 테스트 포함 | `NEXO_FLUTTER_INTEGRATION_TEST=1 bin/test` | `apps/flutter-test`에서 `flutter test integration_test`를 실행한다. Flutter SDK가 필요하다. | +| Android native 단위 테스트 포함 | `NEXO_ANDROID_NATIVE_TEST=1 bin/test` | `apps/flutter-test/android`에서 `./gradlew testDebugUnitTest`를 실행한다. Java/Gradle/Android SDK가 필요하다. | `bin/*` helper는 필요한 로컬 도구가 없으면 일부 검증을 건너뛸 수 있다. 작업 보고에는 건너뛴 검증을 함께 적는다. @@ -171,6 +173,8 @@ upstream 새 기능은 다음 후보로 분류한 뒤 반영한다. | `NEXO_CORE_GO_TEST` | `bin/test`에서 서버 Go 테스트를 실행한다. | `0` | | `NEXO_CORE_GO_LINT` | `bin/lint`에서 서버 Go vet을 실행한다. | `0` | | `NEXO_CORE_GO_BUILD` | `bin/build`에서 서버 Go build를 실행한다. | `0` | +| `NEXO_FLUTTER_INTEGRATION_TEST` | `bin/test`에서 `apps/flutter-test` integration 테스트를 실행한다. | `0` | +| `NEXO_ANDROID_NATIVE_TEST` | `bin/test`에서 `apps/flutter-test/android` Android 네이티브 단위 테스트를 실행한다. | `0` | ## 참고 문서 diff --git a/agent-roadmap/current.md b/agent-roadmap/current.md index 8bacfa71..c2eae592 100644 --- a/agent-roadmap/current.md +++ b/agent-roadmap/current.md @@ -7,7 +7,7 @@ ## 활성 Milestone -- [계획] CI/CD와 운영 업데이트 루프 +- [계획] 내부 검증 파이프라인과 업데이트 대응 루프 - Phase: `agent-roadmap/phase/upstream-runtime/PHASE.md` - 경로: `agent-roadmap/phase/upstream-runtime/milestones/cicd-operations.md` diff --git a/agent-roadmap/phase/upstream-runtime/PHASE.md b/agent-roadmap/phase/upstream-runtime/PHASE.md index 49ff0d85..ea2b5fa0 100644 --- a/agent-roadmap/phase/upstream-runtime/PHASE.md +++ b/agent-roadmap/phase/upstream-runtime/PHASE.md @@ -7,7 +7,7 @@ ## 목표 Mattermost server, webapp, push-proxy를 nexo의 upstream-followable 메시징 런타임으로 유지한다. -보안/버그/기능 업데이트를 계속 흡수할 수 있도록 baseline 기록, patch 경계, CI/CD, 배포 검증, 이슈 대응 루프를 만든다. +보안/버그/기능 업데이트를 계속 흡수할 수 있도록 baseline 기록, patch 경계, 내부 검증, 이슈 대응 루프를 만든다. ## Milestone 흐름 @@ -18,9 +18,9 @@ Mattermost server, webapp, push-proxy를 nexo의 upstream-followable 메시징 - [완료] 업스트림 팔로잉 기준선 - 경로: `agent-roadmap/archive/phase/upstream-runtime/milestones/upstream-following.md` - 요약: server/webapp/push-proxy의 upstream baseline, patch 경계, update 판단 기준을 정한다. -- [계획] CI/CD와 운영 업데이트 루프 +- [계획] 내부 검증 파이프라인과 업데이트 대응 루프 - 경로: `agent-roadmap/phase/upstream-runtime/milestones/cicd-operations.md` - - 요약: upstream update, compose 배포, 검증 실패 이슈화를 반복 가능한 파이프라인으로 만든다. + - 요약: upstream update와 SDK 변경을 내부 검증, compose smoke, 실패 분류 루프로 확인한다. ## Phase 경계 @@ -28,4 +28,4 @@ Mattermost server, webapp, push-proxy를 nexo의 upstream-followable 메시징 - upstream 코드의 대량 rename, deep fork, 제품 UI 재작성은 별도 결정 전 진행하지 않는다. - `services/core/webapp`은 기본 메시지 앱 또는 reference front로 유지하되, nexo embedded SDK와 책임을 섞지 않는다. - `packages/messaging_flutter`는 nexo-owned embedded SDK이며 Mattermost mobile code를 팔로잉하는 대상이 아니다. -- nexo 변경은 compose, docs, CI/CD, patch inventory, 얇은 branding/compatibility layer에 우선 둔다. +- nexo 변경은 compose, docs, 내부 검증, patch inventory, 얇은 branding/compatibility layer에 우선 둔다. diff --git a/agent-roadmap/phase/upstream-runtime/milestones/cicd-operations.md b/agent-roadmap/phase/upstream-runtime/milestones/cicd-operations.md index c99223e6..784bdd6c 100644 --- a/agent-roadmap/phase/upstream-runtime/milestones/cicd-operations.md +++ b/agent-roadmap/phase/upstream-runtime/milestones/cicd-operations.md @@ -1,4 +1,4 @@ -# Milestone: CI/CD와 운영 업데이트 루프 +# Milestone: 내부 검증 파이프라인과 업데이트 대응 루프 ## 위치 @@ -7,8 +7,8 @@ ## 목표 -Mattermost server/webapp/push-proxy update와 nexo Flutter SDK 검증을 반복 가능한 CI/CD와 운영 runbook으로 연결한다. -upstream 업데이트에 따른 이슈를 빠르게 분류하고, 수정/보류/rollback 결정을 남길 수 있는 흐름을 만든다. +Mattermost server/webapp/push-proxy update와 nexo Flutter SDK 검증을 내부 검증 파이프라인과 운영 기록 루프로 연결한다. +upstream 업데이트 또는 SDK 변경 후 깨진 지점을 빠르게 감지하고, 수정/보류/rollback 판단을 남길 수 있는 흐름을 만든다. ## 상태 @@ -20,39 +20,38 @@ upstream 업데이트에 따른 이슈를 빠르게 분류하고, 수정/보류/ ## 구현 잠금 -- 상태: 잠금 +- 상태: 해제 - 결정 필요: - - [ ] CI/CD 실행 위치를 GitHub Actions, 자체 runner, 원격 테스트 서버 중 어디에 둘지 결정한다. - - [ ] production-like 배포 대상과 secret 보관 방식을 결정한다. + - 없음 - 결정 완료: - - [x] 2026-05-27: upstream update를 쉽게 따라가기 위한 CI/CD와 이슈 대응 루프를 프로젝트 방향성에 포함한다. + - [x] 2026-05-27: upstream update를 쉽게 따라가기 위한 검증 자동화와 이슈 대응 루프를 프로젝트 방향성에 포함한다. + - [x] 2026-05-30: 현 단계에서는 외부 Jenkins, production-like 배포, secret 정책, CD 파이프라인을 요구하지 않고 내부 검증 파이프라인을 먼저 만든다. ## 범위 - workspace `bin/test`, `bin/lint`, `bin/build` 기반 기본 검증 - optional Go test/vet/build와 webapp/npm 검증 -- compose smoke, server ping, webapp 접근, push-proxy smoke +- local/internal compose smoke, server ping, webapp 접근, push-proxy smoke - Flutter SDK Dart/integration/Android native test -- upstream update 실패 이슈화, rollback, release note 흐름 +- upstream update 실패 분류, 수정/보류/rollback 판단 기록, release note 입력 흐름 ## 기능 -### Epic: [ci] Continuous verification +### Epic: [ci] Internal verification upstream-followable runtime과 nexo SDK를 같은 변경 흐름에서 검증한다. -- [ ] [workspace-checks] CI에서 `bin/test`, `bin/lint`, `bin/build`를 실행하고 skip된 도구를 보고하는 기준을 만든다. +- [x] [workspace-checks] 내부 검증에서 `bin/test`, `bin/lint`, `bin/build`를 실행하고 skip된 도구를 보고하는 기준을 만든다. - [ ] [core-checks] optional Go test/vet/build와 webapp 검증을 upstream update 또는 core 변경 시 실행하는 기준을 만든다. -- [ ] [compose-smoke] compose로 core, webapp, db, push-proxy를 올리고 최소 smoke를 확인하는 CI 또는 원격 검증 흐름을 만든다. +- [ ] [compose-smoke] compose로 core, webapp, db, push-proxy를 올리고 최소 smoke를 확인하는 local/internal 검증 흐름을 만든다. - [ ] [sdk-checks] Flutter SDK Dart test, client integration test, Android native unit test를 환경별로 나누어 실행하는 기준을 만든다. -### Epic: [cd] Deployment loop +### Epic: [cd] Update response loop -업데이트와 배포 실패가 추적 가능한 이슈로 남도록 운영 루프를 만든다. +업데이트와 검증 실패가 추적 가능한 기록으로 남도록 대응 루프를 만든다. - [ ] [update-branch] Mattermost server/webapp/push-proxy baseline update용 branch, diff, patch inventory 절차를 정리한다. -- [ ] [deploy-pipeline] dev/test/production-like compose 배포, secret 주입, rollback 절차를 정리한다. -- [ ] [issue-loop] upstream update 실패를 server, webapp, push-proxy, SDK contract, infra 중 하나로 분류하고 후속 이슈로 남기는 절차를 만든다. +- [ ] [issue-loop] upstream update 또는 SDK 변경 실패를 server, webapp, push-proxy, SDK contract, infra 중 하나로 분류하고 후속 기록으로 남기는 절차를 만든다. - [ ] [release-note] server/front/push-proxy baseline과 Flutter SDK compatibility를 한 번에 볼 수 있는 release note 기준을 만든다. ## 완료 리뷰 @@ -67,14 +66,14 @@ upstream-followable runtime과 nexo SDK를 같은 변경 흐름에서 검증한 ## 범위 제외 -- production hardening 전체 구현은 배포 대상과 secret 정책 결정 전까지 진행하지 않는다. +- 외부 Jenkins, production-like 배포, secret 관리 정책, CD 파이프라인 구현은 후속 Milestone 전까지 포함하지 않는다. - 외부 공개용 landing page나 marketing site는 포함하지 않는다. - billing, organization management, 별도 admin console은 포함하지 않는다. ## 작업 컨텍스트 - 관련 경로: `.github/`, `bin/`, `services/core/`, `services/core/compose/`, `packages/messaging_flutter/`, `apps/flutter-test/`, `docs/` -- 표준선(선택): 먼저 private/internal deployment loop를 만들고, runner와 secret 정책이 확정되면 production-like CD로 확장한다 +- 표준선: 먼저 local/internal verification loop를 만들고, 외부 runner나 CD는 배포 필요성이 생긴 뒤 별도 Milestone으로 확장한다. - 선행 작업: 업스트림 팔로잉 기준선 -- 후속 작업: 운영 runbook, release/versioning 고도화 -- 확인 필요: CI/CD 실행 위치와 production-like 배포 대상 +- 후속 작업: 운영 runbook, release/versioning 고도화, production-like CD 검토 +- 확인 필요: 없음 diff --git a/bin/build b/bin/build index 22eb130d..627452d9 100755 --- a/bin/build +++ b/bin/build @@ -26,3 +26,15 @@ if [ -f "$root/services/core/server/go.mod" ]; then echo "skip services/core/server: set NEXO_CORE_GO_BUILD=1 to run the Go build" fi fi + +if [ -f "$root/services/core/webapp/package.json" ]; then + if [ "${NEXO_CORE_WEBAPP_BUILD:-0}" = "1" ]; then + if command -v npm >/dev/null 2>&1; then + (cd "$root/services/core/webapp" && npm run build) + else + echo "skip services/core/webapp: npm not found" + fi + else + echo "skip services/core/webapp: set NEXO_CORE_WEBAPP_BUILD=1 to run the webapp npm build" + fi +fi diff --git a/bin/lint b/bin/lint index c7654cb8..8cd730df 100755 --- a/bin/lint +++ b/bin/lint @@ -31,3 +31,15 @@ if [ -f "$root/services/core/server/go.mod" ]; then fi fi +if [ -f "$root/services/core/webapp/package.json" ]; then + if [ "${NEXO_CORE_WEBAPP_LINT:-0}" = "1" ]; then + if command -v npm >/dev/null 2>&1; then + (cd "$root/services/core/webapp" && npm run check) + else + echo "skip services/core/webapp: npm not found" + fi + else + echo "skip services/core/webapp: set NEXO_CORE_WEBAPP_LINT=1 to run the webapp npm check" + fi +fi + diff --git a/bin/smoke-compose b/bin/smoke-compose new file mode 100755 index 00000000..6a6a8605 --- /dev/null +++ b/bin/smoke-compose @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# +# nexo compose smoke helper +# Checks the status of the local Docker Compose runtime and validates key services. +# + +set -euo pipefail + +# Print help/usage if requested +if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then + echo "Usage: bin/smoke-compose [options]" + echo "" + echo "Options:" + echo " -h, --help Show this help message and exit" + echo "" + echo "Checks performed:" + echo " 1. Verify presence of required commands: docker, docker compose, curl, grep" + echo " 2. Verify that Docker Compose services are running (docker compose ps)" + echo " 3. Ping the Mattermost Core API (system/ping)" + echo " 4. Check PostgreSQL database readiness (pg_isready)" + echo " 5. Request Mattermost WebApp index" + echo " 6. Verify push-proxy container status" + echo " 7. Validate that Core points to the compose push-proxy server configuration" + echo "" + echo "Environment Variables:" + echo " NEXO_CORE_PORT Core port (default: 18065)" + exit 0 +fi + +# 1. Check for required commands +echo "Checking required commands..." +MISSING_TOOLS=() + +if ! command -v docker &>/dev/null; then + MISSING_TOOLS+=("docker") +else + if ! docker compose version &>/dev/null; then + MISSING_TOOLS+=("docker compose") + fi +fi + +if ! command -v curl &>/dev/null; then + MISSING_TOOLS+=("curl") +fi + +if ! command -v grep &>/dev/null; then + MISSING_TOOLS+=("grep") +fi + +if [ ${#MISSING_TOOLS[@]} -ne 0 ]; then + echo "error: The following required tools are missing: ${MISSING_TOOLS[*]}" >&2 + exit 1 +fi + +# Locate compose directory relative to this script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +COMPOSE_DIR="$PROJECT_ROOT/services/core/compose" + +if [ ! -d "$COMPOSE_DIR" ]; then + echo "error: Compose directory not found at $COMPOSE_DIR" >&2 + exit 1 +fi + +cd "$COMPOSE_DIR" + +PORT="${NEXO_CORE_PORT:-18065}" + +# 2. Check running compose status +echo "Checking Docker Compose services status..." +if ! docker compose ps; then + echo "error: docker compose services are not running or failed to query" >&2 + exit 1 +fi + +# 3. Ping core system +echo "Pinging Mattermost Core API at http://localhost:${PORT}/api/v4/system/ping..." +if ! curl -fsS "http://localhost:${PORT}/api/v4/system/ping"; then + echo "error: Core API ping failed on port ${PORT}" >&2 + exit 1 +fi + +# 4. Check PostgreSQL database readiness +echo "Checking PostgreSQL database readiness..." +if ! docker compose exec -T db pg_isready -U mmuser -d mattermost; then + echo "error: Database is not ready" >&2 + exit 1 +fi + +# 5. Check WebApp HTTP access +echo "Checking Mattermost WebApp access at http://localhost:${PORT}..." +if ! curl -fsSI "http://localhost:${PORT}"; then + echo "error: WebApp is not accessible on port ${PORT}" >&2 + exit 1 +fi + +# 6. Check Push-Proxy container running status +echo "Checking push-proxy service running status..." +if ! docker compose ps --services --status running push-proxy | grep -x "push-proxy" &>/dev/null; then + echo "error: push-proxy service is not running (stopped or missing)" >&2 + exit 1 +fi + +# 7. Validate Push Notification Server configuration in docker compose config +echo "Verifying push notification server URL configuration..." +if ! docker compose config | grep -E "MM_EMAILSETTINGS_PUSHNOTIFICATIONSERVER[=: ]+[\"']?http://push-proxy:8066[\"']?" &>/dev/null; then + echo "error: Push notification server URL is not configured to http://push-proxy:8066" >&2 + exit 1 +fi + +echo "All smoke checks passed successfully!" diff --git a/bin/test b/bin/test index f564c849..8ea723dc 100755 --- a/bin/test +++ b/bin/test @@ -31,3 +31,66 @@ if [ -f "$root/services/core/server/go.mod" ]; then fi fi +if [ -f "$root/services/core/webapp/package.json" ]; then + if [ "${NEXO_CORE_WEBAPP_TEST:-0}" = "1" ]; then + if command -v npm >/dev/null 2>&1; then + (cd "$root/services/core/webapp" && npm run test-ci) + else + echo "skip services/core/webapp: npm not found" + fi + else + echo "skip services/core/webapp: set NEXO_CORE_WEBAPP_TEST=1 to run the webapp npm test suite" + fi +fi + +if [ -f "$root/apps/flutter-test/pubspec.yaml" ]; then + if [ "${NEXO_FLUTTER_INTEGRATION_TEST:-0}" = "1" ]; then + if command -v flutter >/dev/null 2>&1; then + (cd "$root/apps/flutter-test" && flutter test integration_test) + else + echo "skip apps/flutter-test integration_test: flutter not found" + fi + else + echo "skip apps/flutter-test integration_test: set NEXO_FLUTTER_INTEGRATION_TEST=1 to run integration tests" + fi +fi + +if [ "${NEXO_ANDROID_NATIVE_TEST:-0}" = "1" ]; then + if [ -f "$root/apps/flutter-test/android/gradlew" ]; then + if command -v java >/dev/null 2>&1; then + # Check if Android SDK is available + has_android_sdk=0 + if [ -n "${ANDROID_HOME:-}" ] && [ -d "$ANDROID_HOME" ]; then + has_android_sdk=1 + elif [ -n "${ANDROID_SDK_ROOT:-}" ] && [ -d "$ANDROID_SDK_ROOT" ]; then + has_android_sdk=1 + else + local_sdk_dir="" + if [ -f "$root/apps/flutter-test/android/local.properties" ]; then + # Extract non-commented sdk.dir value + sdk_line=$(grep -E '^[[:space:]]*sdk\.dir[[:space:]]*=' "$root/apps/flutter-test/android/local.properties" || true) + if [ -n "$sdk_line" ]; then + local_sdk_dir=$(echo "$sdk_line" | cut -d'=' -f2- | xargs || true) + fi + fi + if [ -n "$local_sdk_dir" ] && [ -d "$local_sdk_dir" ]; then + has_android_sdk=1 + fi + fi + + if [ "$has_android_sdk" = "1" ]; then + (cd "$root/apps/flutter-test/android" && ./gradlew testDebugUnitTest) + else + echo "skip apps/flutter-test Android native tests: Android SDK not found (ANDROID_HOME, ANDROID_SDK_ROOT, and local.properties sdk.dir are all missing or point to invalid directories)" + fi + else + echo "BLOCKED apps/flutter-test Android native tests: java not found" + fi + else + echo "skip apps/flutter-test Android native tests: gradlew not found" + fi +else + echo "skip apps/flutter-test Android native tests: set NEXO_ANDROID_NATIVE_TEST=1 to run Android native tests" +fi + + diff --git a/docs/README.md b/docs/README.md index ab5cf31a..41aeb1c0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,10 +8,11 @@ Module-specific instructions stay with the module that owns them. ## Runtime Baseline Index - Workspace runtime and upstream policy: `../README.md` +- Internal verification baseline: `../README.md#내부-검증-기준` - Core compose runtime, smoke checks, remote parity, and secret boundary: `../services/core/compose/README.md` - Core upstream snapshot baseline: `../services/core/UPSTREAM.md` - Mattermost mobile app upstream snapshot baseline: `../apps/mattermost/UPSTREAM.md` - Mattermost push-proxy upstream snapshot baseline: `../services/push-proxy/UPSTREAM.md` - Runtime Image Validation details: `runtime-image-validation.md` - +- Compose smoke helper utility: `../bin/smoke-compose` diff --git a/services/core/compose/README.md b/services/core/compose/README.md index 28e1d4e2..fec4bc7e 100644 --- a/services/core/compose/README.md +++ b/services/core/compose/README.md @@ -51,9 +51,8 @@ For the minimum push-proxy smoke, confirm the service is running and the core service still points at the compose-network DNS name: ```sh -docker compose ps push-proxy -docker compose config | rg --fixed-strings \ - "MM_EMAILSETTINGS_PUSHNOTIFICATIONSERVER=http://push-proxy:8066" +docker compose ps --services --status running push-proxy | grep -x "push-proxy" +docker compose config | grep -E "MM_EMAILSETTINGS_PUSHNOTIFICATIONSERVER[=: ]+[\"']?http://push-proxy:8066[\"']?" ``` FCM/APNS credential validation is not part of the default smoke. Run it only