From 42db9b5d00cf1c87ebc89696197be585b3d2e66f Mon Sep 17 00:00:00 2001 From: ttipo Date: Fri, 19 Jun 2026 15:58:46 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20Gitea=20=EB=8F=99=EA=B8=B0=ED=99=94=20?= =?UTF-8?q?=EC=A3=BC=EA=B8=B0=20=EB=A7=A4=EC=8B=9C=EA=B0=84=E2=86=9210?= =?UTF-8?q?=EB=B6=84=20=EB=8B=A8=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RepoSyncService 의 @Cron 을 EVERY_HOUR→EVERY_10_MINUTES 로 변경. Gitea 직접 변경(이름/공개범위/브랜치)의 Relay 반영 지연이 최대 1시간→10분으로 단축. 기동 직후 1회 동기화는 유지, 중복 실행 가드도 그대로. Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/src/modules/repo/repo-sync.service.ts | 8 ++++---- docs/api-contract.md | 2 +- docs/integration-progress.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/modules/repo/repo-sync.service.ts b/backend/src/modules/repo/repo-sync.service.ts index c7d93fd..4256062 100644 --- a/backend/src/modules/repo/repo-sync.service.ts +++ b/backend/src/modules/repo/repo-sync.service.ts @@ -50,10 +50,10 @@ export class RepoSyncService implements OnApplicationBootstrap { void this.syncFromGitea('부트스트랩'); } - // 매시간 정각 동기화 - @Cron(CronExpression.EVERY_HOUR) - async handleHourlySync(): Promise { - await this.syncFromGitea('스케줄(매시간)'); + // 10분마다 동기화 + @Cron(CronExpression.EVERY_10_MINUTES) + async handleScheduledSync(): Promise { + await this.syncFromGitea('스케줄(10분)'); } // 조직 저장소 동기화 본체 — 실패해도 예외를 전파하지 않고 로그만 남긴다. diff --git a/docs/api-contract.md b/docs/api-contract.md index df45cfb..3c1dfb5 100644 --- a/docs/api-contract.md +++ b/docs/api-contract.md @@ -145,7 +145,7 @@ status 매핑보다 우선해 그대로 표준 응답으로 내려보내고, 프 ##### Gitea → Relay 역방향 동기화 (주기적 import) -조직에 직접 만든 저장소도 Relay 에 보이도록, **서버 기동 직후 1회 + 매시간 정각**에 조직 저장소 목록(`GET /api/v1/orgs/{org}/repos`)을 받아 Relay 와 맞춘다(`RepoSyncService`). +조직에 직접 만든 저장소도 Relay 에 보이도록, **서버 기동 직후 1회 + 10분마다**에 조직 저장소 목록(`GET /api/v1/orgs/{org}/repos`)을 받아 Relay 와 맞춘다(`RepoSyncService`). - **추가형 + 역동기화(reconcile)**: Gitea ID → slug 순으로 매칭(이번 동기화에서 이미 매칭된 Relay 저장소는 중복 매칭 제외 — rename 후 옛 이름 재사용 충돌 방지). Relay 에 없으면 import(멤버 0명). 매칭되면 **Gitea 의 네이티브 필드와 1:1 대응되는 값을 Gitea 기준(SSOT)으로 끌어온다**: `slugName`(Gitea rename → Relay 라우팅·쓰기 경로가 새 이름을 따름), `visibility`(private 토글), `branch`(기본 브랜치), + 연동정보(`giteaRepoId`/`giteaFullName`/`cloneUrl`/`htmlUrl`, 누락표시 해제). **단 표시명(`name`)·설명(`description`)은 Gitea 단일 `description` 에 "표시제목 — 설명" 으로 합쳐 저장하므로(표시명 필드 부재), Gitea 에서 description 을 직접 편집하면 표시명이 깨질 수 있어 Relay 가 SSOT 로 유지**(역동기화 제외). Relay→Gitea 쓰기(`PATCH`)는 설명/공개범위에 더해 **기본 브랜치(`default_branch`)도 푸시**해 양방향을 일치시킨다(브랜치는 Gitea 에 실제 존재해야 함). - **import 표시명**: Gitea description 의 `"표시제목 — 설명"` 규칙을 파싱(구분자 없으면 slug 를 표시명, 전체를 설명으로). diff --git a/docs/integration-progress.md b/docs/integration-progress.md index d5a09b3..019d37b 100644 --- a/docs/integration-progress.md +++ b/docs/integration-progress.md @@ -83,7 +83,7 @@ **백엔드** `backend/src/modules/gitea/`, `modules/repo/repo-sync.service.ts` - 문제: 동기화가 단방향(Relay→Gitea)이라 **Gitea 조직에 직접 만든 저장소가 Relay 목록에 안 떴음**. - `GiteaService.listOrgRepos()`: 조직 저장소 페이지네이션 순회(`GET /orgs/{org}/repos`). 결과에 `name`/`description`/`private` 추가. -- `RepoSyncService`: **서버 기동 직후 1회(`OnApplicationBootstrap`, 비차단) + 매시간(`@Cron(EVERY_HOUR)`)** 동기화. `ScheduleModule.forRoot()` 는 `app.module` 에 이미 등록됨. +- `RepoSyncService`: **서버 기동 직후 1회(`OnApplicationBootstrap`, 비차단) + 10분마다(`@Cron(EVERY_10_MINUTES)`)** 동기화. `ScheduleModule.forRoot()` 는 `app.module` 에 이미 등록됨. - **추가형 + 역동기화(reconcile, 2026-06-19 확장)**: Gitea ID→slug 매칭(중복 매칭 가드). 없으면 import(멤버 0명). 매칭되면 Gitea 네이티브 필드와 1:1 대응되는 **slug(rename)·공개범위·기본 브랜치 + 연동정보를 Gitea 기준(SSOT)으로 끌어온다** → Gitea 에서 직접 이름/공개범위/브랜치를 바꿔도 Relay 가 따라가고 쓰기 경로(slugName)도 일치. **표시명/설명은 Gitea 단일 description 합본 구조라 Relay-SSOT 유지**(외부 편집 시 표시명 깨짐 방지). Relay→Gitea 수정은 `default_branch` 도 함께 푸시(양방향 일치). - **누락 표기**: Gitea 에서 사라진 연동 origin 저장소는 삭제하지 않고 `giteaMissing:true` 로만 표기(재출현 시 복구). `Repo.giteaMissing` 컬럼 추가, `RepoResponse`/프론트 `ApiRepo` 에 노출. - 미연동 시 건너뜀 + 중복 실행 가드.