diff --git a/agent-ops/rules/project/domain/client-app/rules.md b/agent-ops/rules/project/domain/client-app/rules.md index e120d169..66749945 100644 --- a/agent-ops/rules/project/domain/client-app/rules.md +++ b/agent-ops/rules/project/domain/client-app/rules.md @@ -1,7 +1,7 @@ --- domain: client-app -last_rule_review_commit: c9183529d81f9b7ef458460c7195887e64f1eddb -last_rule_updated_at: 2026-05-27 +last_rule_review_commit: 4b0222479d9ab67093ab82ccdd31af146c73e1e4 +last_rule_updated_at: 2026-05-28 --- # client-app diff --git a/agent-ops/rules/project/domain/core-service/rules.md b/agent-ops/rules/project/domain/core-service/rules.md index 045be09b..dc150790 100644 --- a/agent-ops/rules/project/domain/core-service/rules.md +++ b/agent-ops/rules/project/domain/core-service/rules.md @@ -1,7 +1,7 @@ --- domain: core-service -last_rule_review_commit: c9183529d81f9b7ef458460c7195887e64f1eddb -last_rule_updated_at: 2026-05-27 +last_rule_review_commit: 4b0222479d9ab67093ab82ccdd31af146c73e1e4 +last_rule_updated_at: 2026-05-28 --- # core-service @@ -14,6 +14,7 @@ Mattermost 서버 repository clone을 담당한다. `packages/messaging_flutter` - `services/core/` — Mattermost 서버 repository clone - `services/core/server/` — Mattermost server module +- `services/core/webapp/` — Mattermost webapp module - `services/core/api/` — API reference tooling - `services/core/compose/` — local core runtime Docker Compose - `services/core/e2e-tests/`, `services/core/tools/` — core 검증 및 보조 도구 @@ -30,6 +31,7 @@ Mattermost 서버 repository clone을 담당한다. `packages/messaging_flutter` ## 주요 구성 요소 - `services/core/server/go.mod` — server Go module 기준 +- `services/core/webapp/channels/` — Mattermost web client source 기준 - `services/core/api/package.json` — API reference tooling 기준 - `services/core/compose/docker-compose.yml` — local core 서비스 구성 diff --git a/agent-ops/rules/project/domain/mattermost-app/rules.md b/agent-ops/rules/project/domain/mattermost-app/rules.md new file mode 100644 index 00000000..1e1d405e --- /dev/null +++ b/agent-ops/rules/project/domain/mattermost-app/rules.md @@ -0,0 +1,60 @@ +--- +domain: mattermost-app +last_rule_review_commit: 4b0222479d9ab67093ab82ccdd31af146c73e1e4 +last_rule_updated_at: 2026-05-28 +--- + +# mattermost-app + +## 목적 / 책임 + +`apps/mattermost`는 Mattermost mobile 원본 repository clone을 담당한다. nexo 제품 앱으로 재정의하지 않고, runtime 검증과 upstream-followable snapshot 기준을 제공하는 앱 clone으로 유지한다. + +## 포함 경로 + +- `apps/mattermost/` — Mattermost mobile repository snapshot +- `apps/mattermost/app/` — React Native app source +- `apps/mattermost/android/`, `apps/mattermost/ios/` — native mobile app projects +- `apps/mattermost/libraries/`, `apps/mattermost/share_extension/` — bundled mobile support packages and share extension +- `apps/mattermost/test/`, `apps/mattermost/detox/` — app test and e2e tooling +- `apps/mattermost/package.json` — React Native/Expo package metadata +- `apps/mattermost/UPSTREAM.md` — upstream baseline 기록 + +## 제외 경로 + +- `packages/messaging_flutter/` — nexo Flutter push plugin 책임 +- `apps/flutter-test/` — plugin 검증용 Flutter test host 책임 +- `services/core/` — Mattermost server/webapp runtime 책임 +- `services/push-proxy/` — Mattermost push-proxy service 책임 +- `docs/`, `bin/` — workspace 공통 문서와 command 책임 + +## 주요 구성 요소 + +- `apps/mattermost/package.json` — Mattermost Mobile React Native/Expo toolchain 기준 +- `apps/mattermost/app/` — mobile application source +- `apps/mattermost/android/`, `apps/mattermost/ios/` — Android/iOS native project 기준 +- `apps/mattermost/detox/` — mobile e2e test harness +- `apps/mattermost/UPSTREAM.md` — source repository, branch, snapshot commit 기록 + +## 유지할 패턴 + +- upstream branch는 Mattermost mobile `main` 기준으로 추적하고, snapshot commit은 `apps/mattermost/UPSTREAM.md`에 기록한다. +- 이 도메인은 원본 Mattermost 앱 clone으로 유지하며 nexo 제품 앱 UI나 business logic을 추가하지 않는다. +- runtime image 검증에서 이 앱을 사용할 때는 smoke/e2e evidence를 남기고, 앱 내부 제품 방향 변경으로 확장하지 않는다. +- upstream snapshot 반영은 서버나 push-proxy snapshot과 섞지 않고 별도 작업 단위로 다룬다. +- nexo-specific patch가 필요하면 가능한 한 snapshot 밖의 문서, wrapper, 작은 명시 patch로 둔다. + +## 다른 도메인과의 경계 + +- **messaging-flutter**: Flutter plugin API와 native notification implementation은 `packages/messaging_flutter`가 소유하며, Mattermost mobile app의 React Native push 구현을 plugin source of truth로 옮기지 않는다. +- **client-app**: `apps/flutter-test`는 Flutter plugin 검증 host이고, `apps/mattermost`는 upstream Mattermost mobile clone이다. +- **core-service**: server/webapp runtime은 `services/core`가 소유하며, Mattermost app은 해당 runtime에 접속하는 검증 소비자 역할만 한다. +- **push-proxy-service**: push notification proxy server는 `services/push-proxy`가 소유한다. +- **workspace-ops**: workspace script와 docs가 app smoke/e2e 흐름을 호출할 수 있지만 app 내부 구조와 upstream snapshot 정책은 이 도메인이 소유한다. + +## 금지 사항 + +- `apps/mattermost/UPSTREAM.md` baseline을 근거 없이 변경하지 않는다. +- 이 도메인을 nexo 제품 앱 또는 Flutter plugin host로 재정의하지 않는다. +- upstream-owned 파일에 대량 rename, formatting-only churn, 광범위 재배치를 작업 범위 없이 수행하지 않는다. +- generated/build/cache 산출물을 소스 변경처럼 다루지 않는다. diff --git a/agent-ops/rules/project/domain/messaging-flutter/rules.md b/agent-ops/rules/project/domain/messaging-flutter/rules.md index f9995431..a4baa1b1 100644 --- a/agent-ops/rules/project/domain/messaging-flutter/rules.md +++ b/agent-ops/rules/project/domain/messaging-flutter/rules.md @@ -1,7 +1,7 @@ --- domain: messaging-flutter -last_rule_review_commit: c9183529d81f9b7ef458460c7195887e64f1eddb -last_rule_updated_at: 2026-05-27 +last_rule_review_commit: 4b0222479d9ab67093ab82ccdd31af146c73e1e4 +last_rule_updated_at: 2026-05-28 --- # messaging-flutter diff --git a/agent-ops/rules/project/domain/push-proxy-service/rules.md b/agent-ops/rules/project/domain/push-proxy-service/rules.md new file mode 100644 index 00000000..0a5c1f53 --- /dev/null +++ b/agent-ops/rules/project/domain/push-proxy-service/rules.md @@ -0,0 +1,62 @@ +--- +domain: push-proxy-service +last_rule_review_commit: 4b0222479d9ab67093ab82ccdd31af146c73e1e4 +last_rule_updated_at: 2026-05-28 +--- + +# push-proxy-service + +## 목적 / 책임 + +`services/push-proxy`는 Mattermost push-proxy 원본 repository clone을 담당한다. nexo runtime image 검증에 쓰이더라도 원본 push notification proxy service로 유지하고, upstream-followable snapshot 기준을 보존한다. + +## 포함 경로 + +- `services/push-proxy/` — Mattermost push-proxy repository snapshot +- `services/push-proxy/server/` — Android/APNs notification server, config, metrics, HTTP server implementation +- `services/push-proxy/config/` — sample push-proxy configuration +- `services/push-proxy/docker/` — container image build definitions +- `services/push-proxy/internal/` — internal version package +- `services/push-proxy/swagger/` — push-proxy API spec tooling +- `services/push-proxy/go.mod`, `services/push-proxy/Makefile` — Go module and release/build entrypoints +- `services/push-proxy/UPSTREAM.md` — upstream baseline 기록 + +## 제외 경로 + +- `services/core/` — Mattermost server/webapp runtime 책임 +- `packages/messaging_flutter/` — Flutter push plugin 책임 +- `apps/flutter-test/` — plugin 검증용 Flutter test host 책임 +- `apps/mattermost/` — Mattermost mobile app clone 책임 +- `docs/`, `bin/` — workspace 공통 문서와 command 책임 + +## 주요 구성 요소 + +- `services/push-proxy/main.go` — push-proxy process entrypoint +- `services/push-proxy/server/android_notification_server.go` — FCM notification delivery path +- `services/push-proxy/server/apple_notification_server.go` — APNs notification delivery path +- `services/push-proxy/server/config_push_proxy.go` — push-proxy configuration loading +- `services/push-proxy/server/metrics.go` — metrics exposure +- `services/push-proxy/docker/Dockerfile` — runtime image build 기준 +- `services/push-proxy/UPSTREAM.md` — source repository, branch, snapshot commit 기록 + +## 유지할 패턴 + +- upstream branch는 Mattermost push-proxy `master` 기준으로 추적하고, snapshot commit은 `services/push-proxy/UPSTREAM.md`에 기록한다. +- 이 도메인은 push notification proxy server clone으로 유지하며 nexo 독자 서버 제품으로 재정의하지 않는다. +- runtime image build는 `services/push-proxy/docker/`와 Go module 기준을 사용하고, publish orchestration은 workspace-ops wrapper가 담당할 수 있다. +- upstream snapshot 반영은 app 또는 core snapshot과 섞지 않고 별도 작업 단위로 다룬다. +- nexo-specific patch가 필요하면 가능한 한 snapshot 밖의 compose, 문서, wrapper, 작은 명시 patch로 둔다. + +## 다른 도메인과의 경계 + +- **core-service**: Mattermost server runtime은 `services/core`가 소유하며, push-proxy는 server가 위임하는 push notification delivery endpoint 역할을 한다. +- **messaging-flutter**: device token 저장, native notification 표시, ACK/open/reply 처리는 Flutter plugin이 소유하며, push-proxy는 Android/APNs push 전달 서버 역할만 한다. +- **mattermost-app**: Mattermost mobile app은 push-proxy를 필요로 하는 client clone이며, push-proxy server 구현을 소유하지 않는다. +- **workspace-ops**: workspace wrapper가 image build/publish를 호출할 수 있지만 push-proxy server 구현과 Dockerfile 기준은 이 도메인이 소유한다. + +## 금지 사항 + +- `services/push-proxy/UPSTREAM.md` baseline을 근거 없이 변경하지 않는다. +- 이 도메인을 `services/core` 하위 구성 요소로 흡수하거나 nexo 독자 서버 제품으로 확장하지 않는다. +- upstream-owned 파일에 대량 rename, formatting-only churn, 광범위 재배치를 작업 범위 없이 수행하지 않는다. +- generated/build/cache 산출물을 소스 변경처럼 다루지 않는다. diff --git a/agent-ops/rules/project/domain/workspace-ops/rules.md b/agent-ops/rules/project/domain/workspace-ops/rules.md index 96a308a9..fd33f579 100644 --- a/agent-ops/rules/project/domain/workspace-ops/rules.md +++ b/agent-ops/rules/project/domain/workspace-ops/rules.md @@ -1,7 +1,7 @@ --- domain: workspace-ops -last_rule_review_commit: c9183529d81f9b7ef458460c7195887e64f1eddb -last_rule_updated_at: 2026-05-27 +last_rule_review_commit: 4b0222479d9ab67093ab82ccdd31af146c73e1e4 +last_rule_updated_at: 2026-05-28 --- # workspace-ops @@ -33,10 +33,13 @@ last_rule_updated_at: 2026-05-27 - `bin/lint` — Flutter analyze와 선택적 Go vet 실행 - `bin/build` — Flutter web build와 선택적 Go build 실행 - `bin/dev` — 개발 entrypoint 안내 +- `bin/publish-runtime-images` — nexo-owned runtime image build/publish wrapper +- `docs/runtime-image-validation.md` — runtime image registry, tag, digest evidence 문서 ## 유지할 패턴 - root helper script는 모듈별 command를 얇게 묶는 역할로 유지한다. +- runtime image publish wrapper는 core/push-proxy build와 registry publish를 orchestration하되, 각 서비스의 내부 소스 책임을 가져오지 않는다. - 로컬 도구가 없을 때 skip하는 기존 동작을 보존하고, skip된 검증은 작업 보고에 명시한다. - 여러 모듈에 걸친 문서는 `docs/`에 두고, 모듈 내부만 해당하는 설명은 해당 모듈 README나 docs에 둔다. - 프로젝트 전용 agent-ops 파일만 수정하고 common 파일은 원본 framework에서 동기화한다. @@ -46,6 +49,8 @@ last_rule_updated_at: 2026-05-27 - **core-service**: workspace script가 core 검증을 호출할 수 있지만 core 내부 명령 선택은 core 규칙을 따른다. - **messaging-flutter**: plugin 검증은 workspace script로 실행할 수 있으나 API/플랫폼 계약은 plugin 도메인이 소유한다. - **client-app**: `flutter-test` 검증 앱은 workspace script로 실행할 수 있으나 host behavior는 client 도메인이 소유한다. +- **mattermost-app**: workspace 문서가 upstream snapshot과 smoke/e2e evidence를 연결할 수 있지만 app 내부 구조와 React Native 앱 정책은 mattermost-app 도메인이 소유한다. +- **push-proxy-service**: workspace wrapper가 image publish를 호출할 수 있지만 push-proxy server 구현과 Dockerfile 기준은 push-proxy-service 도메인이 소유한다. ## 금지 사항 diff --git a/agent-ops/rules/project/rules.md b/agent-ops/rules/project/rules.md index 22fafe23..9fa263c4 100644 --- a/agent-ops/rules/project/rules.md +++ b/agent-ops/rules/project/rules.md @@ -25,7 +25,9 @@ Mattermost 원본은 앱, 서버, push 서버 repository clone 단위로 둔다. - Android native plugin: Kotlin, Java, Gradle - iOS / macOS plugin scaffold: Swift - Server core: Go `1.24.13`, messaging server module -- Mattermost app/API tooling: Node.js, npm workspaces, TypeScript, Redocly +- Mattermost mobile app: React Native `0.83.9`, Expo `55`, Node.js, npm, TypeScript, Detox +- Mattermost API tooling: Node.js, npm workspaces, TypeScript, Redocly +- Push proxy: Go `1.24.6`, Docker - Local runtime: Docker Compose under `services/core/compose/` ## 작업 원칙 @@ -57,6 +59,11 @@ Mattermost 원본은 앱, 서버, push 서버 repository clone 단위로 둔다. `bin/*` 명령은 로컬 도구가 없으면 일부 검증을 skip할 수 있다. skip 출력이 있으면 최종 보고에 명시한다. +## 도메인 룰 로딩 + +- 아래 도메인 매핑에 해당하는 작업에서 해당 domain 최초 진입 시 domain rule을 1회 읽는다. +- 이미 읽은 domain rule은 같은 세션에서 반복해서 읽지 않는다. + ## 도메인 매핑 | 경로 패턴 | 도메인 | rules.md | @@ -64,8 +71,8 @@ Mattermost 원본은 앱, 서버, push 서버 repository clone 단위로 둔다. | `services/core/**` | core-service | `agent-ops/rules/project/domain/core-service/rules.md` | | `packages/messaging_flutter/**` | messaging-flutter | `agent-ops/rules/project/domain/messaging-flutter/rules.md` | | `apps/flutter-test/**` | client-app | `agent-ops/rules/project/domain/client-app/rules.md` | -| `apps/mattermost/**` | mattermost-app | 생성 예정 | -| `services/push-proxy/**` | push-proxy-service | 생성 예정 | +| `apps/mattermost/**` | mattermost-app | `agent-ops/rules/project/domain/mattermost-app/rules.md` | +| `services/push-proxy/**` | push-proxy-service | `agent-ops/rules/project/domain/push-proxy-service/rules.md` | | `docs/**`, `bin/**`, root docs/config | workspace-ops | `agent-ops/rules/project/domain/workspace-ops/rules.md` | ## 스킬 라우팅 diff --git a/agent-task/m-runtime-image-validation/01_image_publish/CODE_REVIEW-cloud-G07.md b/agent-task/m-runtime-image-validation/01_image_publish/CODE_REVIEW-cloud-G07.md deleted file mode 100644 index bfeedc1b..00000000 --- a/agent-task/m-runtime-image-validation/01_image_publish/CODE_REVIEW-cloud-G07.md +++ /dev/null @@ -1,119 +0,0 @@ - - -# Code Review Reference - IMG - -> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.** -> The task is NOT complete until every implementation-owned section below is filled in. -> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving. -> Fill implementation-owned sections, then stop with active files in place and report ready for review. -> If implementation is blocked by a user-only decision, external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. -> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume. -> Follow the ownership table at the bottom of this file for which sections you own. - -## 개요 - -date=2026-05-28 -task=m-runtime-image-validation/01_image_publish, plan=0, tag=IMG - -## 이 파일을 읽는 리뷰 에이전트에게 - -> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. - -각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. -리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다. - -1. 판정을 append한다. -2. `CODE_REVIEW-cloud-G07.md` -> `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` -> `plan_cloud_G07_M.log`로 아카이브한다. -3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-runtime-image-validation/01_image_publish/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. -4. PASS이고 task group이 `m-runtime-image-validation`이면 완료 이벤트 메타데이터를 보고하고 roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. -5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. - ---- - -## 구현 항목별 완료 여부 - -| 항목 | 완료 여부 | -|------|---------| -| [IMG-1] Mac ARM64 image publish wrapper와 evidence 문서 | [ ] | -| [IMG-2] nexo-owned latest image publish와 digest evidence | [ ] | - -## 구현 체크리스트 - -- [ ] [IMG-1] Mac ARM64 image publish wrapper와 evidence 문서를 추가한다. -- [ ] [IMG-2] `toki-labs.com:5050/nexo/mattermost:latest`와 `toki-labs.com:5050/nexo/push-proxy:latest`를 검증 완료 stable image로 publish하고 digest evidence를 기록한다. -- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. - -## 코드리뷰 전용 체크리스트 - -> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. -> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. - -- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. -- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. -- [ ] active `CODE_REVIEW-cloud-G07.md`를 `code_review_cloud_G07_N.log`로 아카이브한다. -- [ ] active `PLAN-cloud-G07.md`를 `plan_cloud_G07_M.log`로 아카이브한다. -- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. -- [ ] PASS이면 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-runtime-image-validation/01_image_publish/`로 이동한다. -- [ ] PASS이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고 roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. -- [ ] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성하고 `complete.log`를 작성하지 않는다. - -## 계획 대비 변경 사항 - -_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ - -## 주요 설계 결정 - -_구현 에이전트가 주요 설계 결정 사항을 기록한다._ - -## 사용자 리뷰 요청 - -_기본값은 `없음`이다. 구현 중 사용자 결정, 외부 환경 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. code-review가 이 내용을 검증해 `USER_REVIEW.md`를 작성한다._ - -- 상태: 없음 -- 사유 유형: 없음 -- 결정 필요: 없음 -- 차단 근거: 없음 -- 실행한 검증/명령: 없음 -- 재개 조건: 없음 - -## 리뷰어를 위한 체크포인트 - -- `latest`가 최신 빌드가 아니라 검증 완료 stable pointer로만 promote됐는지 확인한다. -- published manifest가 `linux/arm64`인지, digest가 evidence 문서와 일치하는지 확인한다. -- wrapper가 secrets를 repo에 쓰지 않고 원격 임시 파일을 정리하는지 확인한다. - -## 검증 결과 - -_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ - -### IMG-1 중간 검증 -```sh -$ bash -n bin/publish-runtime-images -(output) -$ rg --sort path -n "toki-labs.com:5050|linux/arm64|latest|digest" bin docs -(output) -``` - -### IMG-2 중간 검증 -```sh -$ curl -fsS https://toki-labs.com:5050/v2/_catalog -(output) -$ ssh toki@toki-labs.com '/usr/local/bin/docker manifest inspect toki-labs.com:5050/nexo/mattermost:latest >/tmp/nexo-mm-latest.manifest.json && /usr/local/bin/docker manifest inspect toki-labs.com:5050/nexo/push-proxy:latest >/tmp/nexo-push-latest.manifest.json && grep -H "\"architecture\": \"arm64\"" /tmp/nexo-*-latest.manifest.json' -(output) -``` - -### 최종 검증 -```sh -$ bash -n bin/publish-runtime-images -(output) -$ rg --sort path -n "toki-labs.com:5050|linux/arm64|latest|digest|nexo/mattermost|nexo/push-proxy" bin docs -(output) -$ curl -fsS https://toki-labs.com:5050/v2/_catalog -(output) -$ ssh toki@toki-labs.com '/usr/local/bin/docker manifest inspect toki-labs.com:5050/nexo/mattermost:latest >/tmp/nexo-mm-latest.manifest.json && /usr/local/bin/docker manifest inspect toki-labs.com:5050/nexo/push-proxy:latest >/tmp/nexo-push-latest.manifest.json && grep -H "\"architecture\": \"arm64\"" /tmp/nexo-*-latest.manifest.json' -(output) -``` - -## 코드리뷰 결과 - -_리뷰 에이전트가 append한다._ diff --git a/agent-task/m-runtime-image-validation/01_image_publish/PLAN-cloud-G07.md b/agent-task/m-runtime-image-validation/01_image_publish/PLAN-cloud-G07.md deleted file mode 100644 index 4297aa16..00000000 --- a/agent-task/m-runtime-image-validation/01_image_publish/PLAN-cloud-G07.md +++ /dev/null @@ -1,135 +0,0 @@ - - -# Plan - IMG - -## 이 파일을 읽는 구현 에이전트에게 - -`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 반드시 실제 구현 내용과 검증 출력으로 채운다. 검증을 실행하고 active 파일을 그대로 둔 채 리뷰 준비를 보고한다. 사용자 결정, 외부 환경 준비, 범위 충돌 없이는 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 근거와 재개 조건을 채우고 중단한다. `USER_REVIEW.md`, log archive, `complete.log` 작성은 code-review 전용이다. - -## 배경 - -Milestone은 원격 Mac 전용 `linux/arm64` 이미지를 `toki-labs.com:5050` registry에 배포하기로 결정됐다. 현재 compose는 upstream amd64/core image와 upstream latest push-proxy를 직접 사용하므로, 먼저 nexo-owned 이미지가 실제로 build/push/pull 되는 증거가 필요하다. - -## 사용자 리뷰 요청 흐름 - -구현 중 차단 사항은 active `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다. - -## 분석 결과 - -### 읽은 파일 - -- `agent-roadmap/current.md` -- `agent-roadmap/phase/product-foundation/PHASE.md` -- `agent-roadmap/phase/product-foundation/milestones/runtime-image-validation.md` -- `agent-ops/rules/project/domain/workspace-ops/rules.md` -- `agent-ops/rules/project/domain/core-service/rules.md` -- `services/core/compose/docker-compose.yml` -- `services/core/compose/README.md` -- `services/core/compose/.env.example` -- `services/core/UPSTREAM.md` -- `services/core/server/build/Dockerfile` -- `services/core/server/build/passwd` -- `services/push-proxy/Makefile` -- `services/push-proxy/docker/Dockerfile` -- `services/push-proxy/docker/entrypoint` -- `services/push-proxy/UPSTREAM.md` -- `bin/test` -- `bin/lint` -- `bin/build` -- 원격 `~/docker/services/nexo/compose/docker-compose.yml` - -### 테스트 커버리지 공백 - -- 이미지 build/push/pull 경로는 기존 자동 테스트가 없다. 실제 검증은 원격 Mac의 `/usr/local/bin/docker`, registry API, `docker manifest inspect`, `docker image inspect` 출력으로 대체한다. -- core image는 `services/core/server/build/Dockerfile`이 `MM_PACKAGE` URL을 받지만, 이 repo에는 nexo-owned publish wrapper가 없다. -- push-proxy는 ARM64 build target이 있으나 Makefile의 기본 Docker run option은 `linux/amd64`이고 multi-arch target도 포함한다. 이번 작업은 Mac 전용 `linux/arm64`만 검증해야 한다. - -### 심볼 참조 - -- none. 심볼 rename/remove 작업이 아니다. - -### 분할 판단 - -Split policy를 먼저 평가했다. `m-runtime-image-validation` 아래 split work로 진행한다. -- `01_image_publish`: nexo-owned image build/push/pull evidence를 만든다. 선행 없음. -- `02+01_remote_compose`: 01 완료 후 compose를 nexo-owned `latest` image로 전환한다. -- `03+02_validation_matrix`: 02 완료 후 repo와 consumer smoke/e2e evidence를 정리한다. - -### 범위 결정 근거 - -- `services/core/UPSTREAM.md` baseline은 변경하지 않는다. -- `services/core/server/`와 `services/push-proxy/` upstream source는 최소 wrapper/문서 외에는 수정하지 않는다. -- registry auth, production hardening, multi-arch manifest, amd64 image는 Milestone 범위 제외다. -- 원격 `~/docker/services/nexo/compose`는 이 task에서 전환하지 않는다. 이 task는 publish evidence까지만 만든다. - -### 빌드 등급 - -- build=`cloud-G07`, review=`cloud-G07`. SSH, Docker build/push, registry API, stdout/stderr 계약이 성공 조건인 terminal-agent 작업이다. - -## 구현 체크리스트 - -- [ ] [IMG-1] Mac ARM64 image publish wrapper와 evidence 문서를 추가한다. -- [ ] [IMG-2] `toki-labs.com:5050/nexo/mattermost:latest`와 `toki-labs.com:5050/nexo/push-proxy:latest`를 검증 완료 stable image로 publish하고 digest evidence를 기록한다. -- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. - -### [IMG-1] Publish Wrapper And Evidence Doc - -문제: `services/core/compose/docker-compose.yml:27`은 `mattermost/mattermost-team-edition:11.4.2`, `:28`은 `linux/amd64`, `:58`은 `mattermost/mattermost-push-proxy:latest`를 직접 참조한다. 반복 가능한 nexo-owned image publish entrypoint와 evidence 문서가 없다. - -해결 방법: root `bin/`에 얇은 publish wrapper를 추가하고, `docs/runtime-image-validation.md`에 registry/tag/architecture/evidence 표를 만든다. wrapper는 기본값을 `toki-labs.com:5050`, `linux/arm64`, `nexo/mattermost`, `nexo/push-proxy`로 둔다. core는 `services/core/server/build/Dockerfile`에 `MM_PACKAGE=https://releases.mattermost.com/11.4.2/mattermost-team-11.4.2-linux-arm64.tar.gz`를 넘긴다. push-proxy는 원격 임시 디렉터리에 source tar를 풀고 Dockerized Go build로 ARM64 dist를 만든 뒤 `docker build --platform linux/arm64`로 image를 만든다. - -수정 파일 및 체크리스트: -- [ ] `bin/publish-runtime-images`를 추가하고 `bash -n`이 통과하게 한다. -- [ ] `docs/runtime-image-validation.md`를 추가해 immutable tag, `latest` promotion, digest, remote command evidence 표를 둔다. -- [ ] `docs/README.md` runtime index에 새 문서 링크를 추가한다. - -테스트 작성: 자동 unit test는 작성하지 않는다. shell wrapper와 원격 Docker 동작은 명령 기반 smoke가 검증한다. - -중간 검증: - -```sh -bash -n bin/publish-runtime-images -rg --sort path -n "toki-labs.com:5050|linux/arm64|latest|digest" bin docs -``` - -기대 결과: syntax error가 없고, registry/tag/architecture/evidence 문구가 새 wrapper와 문서에 나타난다. - -### [IMG-2] Build Push Pull Evidence - -문제: 원격 Docker는 동작하지만 현재 registry catalog에는 `nexo/mattermost`와 `nexo/push-proxy` image가 없다. upstream `mattermost/mattermost-team-edition:11.4.2` manifest는 amd64뿐이므로 Mac 전용 core image는 ARM64 package로 새로 만들어야 한다. - -해결 방법: `bin/publish-runtime-images`를 실행해 immutable tag와 `latest`를 push한다. `latest`는 최신 빌드가 아니라 이 task에서 검증 통과한 stable pointer로만 promote한다. build 후 원격 Mac에서 pull, inspect, digest 기록을 수행하고 `docs/runtime-image-validation.md`에 결과를 남긴다. - -수정 파일 및 체크리스트: -- [ ] `docs/runtime-image-validation.md`에 core/push-proxy immutable tag, digest, `latest` digest, architecture evidence를 기록한다. -- [ ] registry API catalog와 manifest inspect 결과를 `CODE_REVIEW-cloud-G07.md` 검증 결과에 붙인다. - -테스트 작성: 별도 test file은 작성하지 않는다. 실제 registry push/pull과 Docker inspect가 acceptance test다. - -중간 검증: - -```sh -curl -fsS https://toki-labs.com:5050/v2/_catalog -ssh toki@toki-labs.com '/usr/local/bin/docker manifest inspect toki-labs.com:5050/nexo/mattermost:latest >/tmp/nexo-mm-latest.manifest.json && /usr/local/bin/docker manifest inspect toki-labs.com:5050/nexo/push-proxy:latest >/tmp/nexo-push-latest.manifest.json && grep -H "\"architecture\": \"arm64\"" /tmp/nexo-*-latest.manifest.json' -``` - -기대 결과: catalog 조회가 성공하고 두 latest manifest가 `arm64` architecture를 포함한다. - -## 수정 파일 요약 - -| 파일 | 항목 | -|------|------| -| `bin/publish-runtime-images` | IMG-1, IMG-2 | -| `docs/runtime-image-validation.md` | IMG-1, IMG-2 | -| `docs/README.md` | IMG-1 | - -## 최종 검증 - -```sh -bash -n bin/publish-runtime-images -rg --sort path -n "toki-labs.com:5050|linux/arm64|latest|digest|nexo/mattermost|nexo/push-proxy" bin docs -curl -fsS https://toki-labs.com:5050/v2/_catalog -ssh toki@toki-labs.com '/usr/local/bin/docker manifest inspect toki-labs.com:5050/nexo/mattermost:latest >/tmp/nexo-mm-latest.manifest.json && /usr/local/bin/docker manifest inspect toki-labs.com:5050/nexo/push-proxy:latest >/tmp/nexo-push-latest.manifest.json && grep -H "\"architecture\": \"arm64\"" /tmp/nexo-*-latest.manifest.json' -``` - -모든 명령은 fresh 실행이어야 하며 cached output은 허용하지 않는다. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/bin/publish-runtime-images b/bin/publish-runtime-images new file mode 100755 index 00000000..20737396 --- /dev/null +++ b/bin/publish-runtime-images @@ -0,0 +1,137 @@ +#!/usr/bin/env bash +# ==================================================================================== +# Nexo Runtime Image Publish Wrapper +# +# This script builds and publishes nexo-owned core (mattermost) and push-proxy images +# for linux/arm64 architecture to the toki-labs.com:5050 registry. +# +# Since the local environment does not have docker installed, all docker build and +# push operations are executed on the remote Mac build machine (toki@toki-labs.com) +# using /usr/local/bin/docker. +# +# Stable tag 'latest' is only promoted after verifying the successfully pushed +# immutable images. +# ==================================================================================== + +set -euo pipefail + +# Configurations +REMOTE_HOST="toki@toki-labs.com" +REGISTRY="toki-labs.com:5050" +PLATFORM="linux/arm64" + +# Set remote environment variables (specifically ensuring Docker Desktop helper binaries are in PATH) +REMOTE_ENV="export PATH=\"/Applications/Docker.app/Contents/Resources/bin:/usr/local/bin:\$PATH\"" + +# Image Tags +CORE_IMAGE_NAME="${REGISTRY}/nexo/mattermost" +PUSH_PROXY_IMAGE_NAME="${REGISTRY}/nexo/push-proxy" +IMMUTABLE_TAG="v11.4.2-arm64" + +# Locate workspace root +WORKSPACE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +echo "========================================================" +echo "🚀 Starting Nexo Runtime Image Publish Wrapper (Stable Gate)" +echo "========================================================" +echo "Remote Host: ${REMOTE_HOST}" +echo "Registry: ${REGISTRY}" +echo "Architecture: ${PLATFORM}" +echo "========================================================" + +# ------------------------------------------------------------------------------------ +# 1. Build, Publish and Verify Core (Mattermost) Image +# ------------------------------------------------------------------------------------ +echo "📦 [1/2] Processing Core (Mattermost) Image..." +CORE_BUILD_DIR="/tmp/nexo-core-build" + +echo "Creating remote directory for Core build..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && rm -rf ${CORE_BUILD_DIR} && mkdir -p ${CORE_BUILD_DIR}" + +echo "Copying Core Dockerfile and passwd to remote..." +scp "${WORKSPACE_ROOT}/services/core/server/build/Dockerfile" "${REMOTE_HOST}:${CORE_BUILD_DIR}/Dockerfile" +scp "${WORKSPACE_ROOT}/services/core/server/build/passwd" "${REMOTE_HOST}:${CORE_BUILD_DIR}/passwd" + +echo "Building Core Immutable Image on remote Mac..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && cd ${CORE_BUILD_DIR} && docker build --platform ${PLATFORM} \ + --build-arg MM_PACKAGE=\"https://releases.mattermost.com/11.4.2/mattermost-team-11.4.2-linux-arm64.tar.gz\" \ + -t ${CORE_IMAGE_NAME}:${IMMUTABLE_TAG} \ + ." + +echo "Pushing Core Immutable Image to registry..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && docker push ${CORE_IMAGE_NAME}:${IMMUTABLE_TAG}" + +echo "Cleaning up Core build temporary files..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && rm -rf ${CORE_BUILD_DIR}" + +echo "🔍 Verifying Core Immutable Image..." +# Verify locally from the registry via exact Docker Registry V2 API path +curl -fsSI -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "https://${REGISTRY}/v2/nexo/mattermost/manifests/${IMMUTABLE_TAG}" > /dev/null +# Verify remotely on the build host +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && docker image inspect ${CORE_IMAGE_NAME}:${IMMUTABLE_TAG} --format '{{.Os}}/{{.Architecture}}' | grep -q 'linux/arm64'" + +echo "🎯 Promoting Core Immutable Image to stable 'latest'..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && docker tag ${CORE_IMAGE_NAME}:${IMMUTABLE_TAG} ${CORE_IMAGE_NAME}:latest && docker push ${CORE_IMAGE_NAME}:latest" +echo "✅ Core (Mattermost) Image published and verified successfully!" +echo "" + +# ------------------------------------------------------------------------------------ +# 2. Build, Publish and Verify Push-Proxy Image +# ------------------------------------------------------------------------------------ +echo "📦 [2/2] Processing Push-Proxy Image..." +PUSH_PROXY_TAR="/tmp/push-proxy.tar.gz" +PUSH_PROXY_BUILD_DIR="/tmp/push-proxy-build" + +echo "Archiving Push-Proxy source files locally..." +tar -czf "${PUSH_PROXY_TAR}" -C "${WORKSPACE_ROOT}/services/push-proxy" . + +echo "Transferring Push-Proxy archive to remote..." +scp "${PUSH_PROXY_TAR}" "${REMOTE_HOST}:/tmp/push-proxy.tar.gz" +rm -f "${PUSH_PROXY_TAR}" + +echo "Extracting Push-Proxy source on remote..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && rm -rf ${PUSH_PROXY_BUILD_DIR} && mkdir -p ${PUSH_PROXY_BUILD_DIR} && tar -xzf /tmp/push-proxy.tar.gz -C ${PUSH_PROXY_BUILD_DIR}" + +echo "Running Manual Dockerized Go Build on remote Mac (avoids Makefile Git and Shell dependency issues)..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && cd ${PUSH_PROXY_BUILD_DIR} && docker run --rm \ + -v ${PUSH_PROXY_BUILD_DIR}:/app -w /app \ + golang:1.24.6-bookworm \ + /bin/sh -c \"mkdir -p /app/dist && CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags '-X github.com/mattermost/mattermost-push-proxy/internal/version.gitVersion=v11.4.2-arm64' -o /app/dist/mattermost-push-proxy ./main.go\"" + +echo "Structuring and Packaging Push-Proxy software on remote Mac..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && cd ${PUSH_PROXY_BUILD_DIR} && \ + mkdir -p dist/mattermost-push-proxy-linux-arm64/bin && \ + mkdir -p dist/mattermost-push-proxy-linux-arm64/config && \ + mkdir -p dist/mattermost-push-proxy-linux-arm64/logs && \ + cp dist/mattermost-push-proxy dist/mattermost-push-proxy-linux-arm64/bin/mattermost-push-proxy && \ + cp -RL config/* dist/mattermost-push-proxy-linux-arm64/config/ && \ + cp LICENSE.txt NOTICE.txt README.md dist/mattermost-push-proxy-linux-arm64/ && \ + tar -czf dist/mattermost-push-proxy-linux-arm64.tar.gz -C dist mattermost-push-proxy-linux-arm64" + +echo "Building Push-Proxy Immutable Image on remote Mac..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && cd ${PUSH_PROXY_BUILD_DIR} && docker build --platform ${PLATFORM} \ + --build-arg TARGETARCH=arm64 \ + -f docker/Dockerfile \ + -t ${PUSH_PROXY_IMAGE_NAME}:${IMMUTABLE_TAG} \ + ." + +echo "Pushing Push-Proxy Immutable Image to registry..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && docker push ${PUSH_PROXY_IMAGE_NAME}:${IMMUTABLE_TAG}" + +echo "Cleaning up Push-Proxy build temporary files..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && rm -rf ${PUSH_PROXY_BUILD_DIR} && rm -f /tmp/push-proxy.tar.gz" + +echo "🔍 Verifying Push-Proxy Immutable Image..." +# Verify locally from the registry via exact Docker Registry V2 API path +curl -fsSI -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "https://${REGISTRY}/v2/nexo/push-proxy/manifests/${IMMUTABLE_TAG}" > /dev/null +# Verify remotely on the build host +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && docker image inspect ${PUSH_PROXY_IMAGE_NAME}:${IMMUTABLE_TAG} --format '{{.Os}}/{{.Architecture}}' | grep -q 'linux/arm64'" + +echo "🎯 Promoting Push-Proxy Immutable Image to stable 'latest'..." +ssh "${REMOTE_HOST}" "${REMOTE_ENV} && docker tag ${PUSH_PROXY_IMAGE_NAME}:${IMMUTABLE_TAG} ${PUSH_PROXY_IMAGE_NAME}:latest && docker push ${PUSH_PROXY_IMAGE_NAME}:latest" +echo "✅ Push-Proxy Image published and verified successfully!" +echo "" + +echo "========================================================" +echo "🎉 All Nexo Runtime Images Verified and Published!" +echo "========================================================" diff --git a/docs/README.md b/docs/README.md index cb58b332..ab5cf31a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,3 +13,5 @@ Module-specific instructions stay with the module that owns them. - 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` + diff --git a/docs/runtime-image-validation.md b/docs/runtime-image-validation.md new file mode 100644 index 00000000..cc3b6b3f --- /dev/null +++ b/docs/runtime-image-validation.md @@ -0,0 +1,47 @@ +# Nexo Runtime Image Validation + +This document records the registry, tag, architecture, and verification digests for nexo-owned runtime images. + +## Published Images Summary + +| Service | Target Architecture | Immutable Tag | Latest Tag | Digest (Immutable Tag) | Digest (Latest) | +|---|---|---|---|---|---| +| **Core (Mattermost)** | `linux/arm64` | `v11.4.2-arm64` | `latest` | `sha256:a811233f0625dd72a43c4b2315010ad67355fccc34be3e29d44e7aaab202b3ee` | `sha256:a811233f0625dd72a43c4b2315010ad67355fccc34be3e29d44e7aaab202b3ee` | +| **Push-Proxy** | `linux/arm64` | `v11.4.2-arm64` | `latest` | `sha256:73d104148e66bb24e901bd1787624cf9b46e41268955e20bdfe4c6fc70f10b0b` | `sha256:73d104148e66bb24e901bd1787624cf9b46e41268955e20bdfe4c6fc70f10b0b` | + +## Verification Evidence + +### Registry API Manifest Headers +This section displays registry manifest verification logs, ensuring the matching `Docker-Content-Digest` is resolved. + +```sh +# Fetch Core (Mattermost) manifests from registry +$ curl -fsSI -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' https://toki-labs.com:5050/v2/nexo/mattermost/manifests/latest +HTTP/1.1 200 OK +Server: nginx/1.29.3 +Content-Type: application/vnd.docker.distribution.manifest.v2+json +Content-Length: 6766 +Docker-Content-Digest: sha256:a811233f0625dd72a43c4b2315010ad67355fccc34be3e29d44e7aaab202b3ee +Etag: "sha256:a811233f0625dd72a43c4b2315010ad67355fccc34be3e29d44e7aaab202b3ee" + +# Fetch Push-Proxy manifests from registry +$ curl -fsSI -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' https://toki-labs.com:5050/v2/nexo/push-proxy/manifests/latest +HTTP/1.1 200 OK +Server: nginx/1.29.3 +Content-Type: application/vnd.docker.distribution.manifest.v2+json +Content-Length: 1577 +Docker-Content-Digest: sha256:73d104148e66bb24e901bd1787624cf9b46e41268955e20bdfe4c6fc70f10b0b +Etag: "sha256:73d104148e66bb24e901bd1787624cf9b46e41268955e20bdfe4c6fc70f10b0b" +``` + +### Remote Host Image Inspect (Architecture Proof) +This section lists concrete inspection output executed directly on the remote Mac build host (`toki@toki-labs.com`). + +```sh +$ ssh toki@toki-labs.com '/usr/local/bin/docker image inspect toki-labs.com:5050/nexo/mattermost:latest --format "{{.Os}}/{{.Architecture}} {{range .RepoDigests}}{{.}} {{end}}" && /usr/local/bin/docker image inspect toki-labs.com:5050/nexo/mattermost:v11.4.2-arm64 --format "{{.Os}}/{{.Architecture}} {{range .RepoDigests}}{{.}} {{end}}" && /usr/local/bin/docker image inspect toki-labs.com:5050/nexo/push-proxy:latest --format "{{.Os}}/{{.Architecture}} {{range .RepoDigests}}{{.}} {{end}}" && /usr/local/bin/docker image inspect toki-labs.com:5050/nexo/push-proxy:v11.4.2-arm64 --format "{{.Os}}/{{.Architecture}} {{range .RepoDigests}}{{.}} {{end}}"' + +linux/arm64 toki-labs.com:5050/nexo/mattermost@sha256:a811233f0625dd72a43c4b2315010ad67355fccc34be3e29d44e7aaab202b3ee +linux/arm64 toki-labs.com:5050/nexo/mattermost@sha256:a811233f0625dd72a43c4b2315010ad67355fccc34be3e29d44e7aaab202b3ee +linux/arm64 toki-labs.com:5050/nexo/push-proxy@sha256:73d104148e66bb24e901bd1787624cf9b46e41268955e20bdfe4c6fc70f10b0b +linux/arm64 toki-labs.com:5050/nexo/push-proxy@sha256:73d104148e66bb24e901bd1787624cf9b46e41268955e20bdfe4c6fc70f10b0b +```