@@ -8,6 +8,15 @@ const OPENAI_REASONING_EFFORT = "medium"
88const MAX_RELEASE_NOTE_LENGTH = 16_000
99const STABLE_VERSION_PATTERN = / ^ v ? ( \d + ) \. ( \d + ) \. ( \d + ) $ /
1010const DIRECT_PACKAGE_PATTERN = / \. p a c k a g e \( \s * u r l : \s * " (?< url > [ ^ " ] + ) " \s * , \s * (?< requirement > \. e x a c t \( " (?< exact > [ ^ " ] + ) " \) | \. u p T o N e x t M i n o r \( f r o m : \s * " (?< upToNextMinor > [ ^ " ] + ) " \) ) \s * \) / gs
11+ const SEOUL_DATE_TIME_FORMATTER = new Intl . DateTimeFormat ( "en-CA" , {
12+ timeZone : "Asia/Seoul" ,
13+ year : "numeric" ,
14+ month : "2-digit" ,
15+ day : "2-digit" ,
16+ hour : "2-digit" ,
17+ minute : "2-digit" ,
18+ hourCycle : "h23" ,
19+ } )
1120
1221// ThirdParty에 직접 선언한 외부 라이브러리 요구 조건 읽기
1322export function parsePackages ( manifest ) {
@@ -137,15 +146,19 @@ export async function decideCandidates({
137146export function renderPrBodySection ( { runId, packages, now = new Date ( ) } ) {
138147 const applied = packages . filter ( packageInfo => packageInfo . action === "apply" )
139148 const manual = packages . filter ( packageInfo => packageInfo . action === "manual_review" )
140- const date = now . toISOString ( ) . replace ( "T" , " " ) . replace ( / \. \d { 3 } Z $ / , " UTC" )
141149 const lines = [
142150 `<!-- dependency-update:${ runId } -->` ,
143- `### ${ date } 의존성 갱신 실행` ,
151+ "### 실행 시간" ,
152+ "" ,
153+ seoulDateTime ( now ) ,
154+ "" ,
155+ "### 반영 후보" ,
144156 "" ,
145157 ]
146158
147- if ( applied . length !== 0 ) {
148- lines . push ( "#### 반영 후보" , "" )
159+ if ( applied . length === 0 ) {
160+ lines . push ( "없음" , "" )
161+ } else {
149162 lines . push ( "| package | 이전 | 이후 | 근거 |" )
150163 lines . push ( "| --- | --- | --- | --- |" )
151164 lines . push ( ...applied . map ( packageInfo => [
@@ -157,20 +170,31 @@ export function renderPrBodySection({ runId, packages, now = new Date() }) {
157170 lines . push ( "" )
158171 }
159172
160- if ( manual . length !== 0 ) {
161- lines . push ( "#### 수동 확인 필요" , "" )
162- lines . push ( ...manual . map ( packageInfo => [
173+ lines . push ( "### 수동 확인 필요" , "" )
174+ if ( manual . length === 0 ) {
175+ lines . push ( "없음" )
176+ } else {
177+ lines . push ( ...manual . flatMap ( packageInfo => [
163178 `- ${ packageInfo . repository } ` ,
164- `${ packageInfo . currentVersion } → ${ packageInfo . candidateVersion } ` ,
165- packageInfo . manualReviewReason ,
166- packageInfo . releaseNotes ?. url ,
167- ] . filter ( Boolean ) . join ( " — " ) ) )
168- lines . push ( "" )
179+ ` - ${ packageInfo . currentVersion } → ${ packageInfo . candidateVersion } ` ,
180+ ` - ${ packageInfo . manualReviewReason || "변경 이력 확인 필요" } ` ,
181+ ...( packageInfo . releaseNotes ?. url ? [ ` - ${ packageInfo . releaseNotes . url } ` ] : [ ] ) ,
182+ ] ) )
169183 }
170184
171185 return `${ lines . join ( "\n" ) . trimEnd ( ) } \n`
172186}
173187
188+ // 서울 시간대로 실행 시간을 표기
189+ function seoulDateTime ( now ) {
190+ const values = Object . fromEntries (
191+ SEOUL_DATE_TIME_FORMATTER . formatToParts ( now )
192+ . map ( part => [ part . type , part . value ] )
193+ )
194+
195+ return `${ values . year } -${ values . month } -${ values . day } ${ values . hour } :${ values . minute } KST`
196+ }
197+
174198// 직접 선언한 외부 라이브러리의 동일 메이저 후보와 변경 이력 수집
175199export async function discoverCandidates ( {
176200 manifest,
0 commit comments