Skip to content
Merged
48 changes: 48 additions & 0 deletions .github/template/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name-template: 'v$RESOLVED_VERSION'
tag-template: 'v$RESOLVED_VERSION'
version-template: $MAJOR.$MINOR.$PATCH

categories:
- title: '🚀 기능 추가'
labels:
- 'feat'
- title: '♻️ 리팩토링'
labels:
- 'refactor'
- title: '🐛 버그 수정'
labels:
- 'fix'
- title: '🧰 그 외'
labels:
- 'chore'
- 'ci/cd'
- 'docs'
- 'style'
- 'build'
- 'common'
- 'test'

version-resolver:
major:
labels:
- 'major'
minor:
labels:
- 'minor'
patch:
labels:
- 'patch'
default: minor

exclude-labels:
- 'Release'

change-template: '- $TITLE (#$NUMBER) - @$AUTHOR'
change-title-escapes: '\\<*_&'

template: |
## ✨ 변경 사항
$CHANGES

## 📦 전체 변경 이력
[$PREVIOUS_TAG ~ v$RESOLVED_VERSION](<https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION>)
58 changes: 58 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: CI/CD Pipeline

on:
push:
branches: [develop, main]
pull_request:
branches: [develop, main]

# 동일한 브랜치에서 새로운 Push가 발생하면 이전 실행을 취소하여 자원 절약
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: false

- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install

- name: Lint
run: pnpm lint

- name: Build
run: pnpm build
env:
VITE_KAKAO_REST_API_KEY: ${{ secrets.VITE_KAKAO_REST_API_KEY }}
VITE_KAKAO_REDIRECT_URI: ${{ secrets.VITE_KAKAO_REDIRECT_URI }}
VITE_API_BASE_URL: ${{ secrets.VITE_API_BASE_URL }}
36 changes: 36 additions & 0 deletions .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Release Drafter

on:
push:
branches:
- release/*
- main
# PR 이벤트 추가로 자동화 범위 확대
pull_request:
types: [opened, reopened, synchronize]
Comment on lines +3 to +10
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

pull_request 트리거에 브랜치 필터 없음 + 포크 PR 권한 문제

두 가지 연관된 문제가 있습니다:

  1. 브랜치 필터 미지정: pull_request 이벤트에 branches 필터가 없어, 기능 브랜치(예: feature/* → feature/*) 대상 PR 포함 모든 PR에서 워크플로우가 실행됩니다. 불필요한 실행을 줄이려면 대상 브랜치를 명시하는 것이 좋습니다.

  2. 포크 PR 권한 부족: pull_request 이벤트는 포크에서 생성된 PR에 대해 GITHUB_TOKEN이 읽기 전용으로 제한됩니다. "Update Draft Release" 스텝은 contents: write 권한이 필요하므로, 외부 기여자의 포크 PR에서 실패할 수 있습니다. pull_request_target 이벤트를 사용하면 포크 PR에서도 쓰기 권한이 유지됩니다. 단, 이 이벤트는 PR 브랜치가 아닌 베이스 브랜치에서 실행됩니다.

✨ 개선 제안
 on:
   push:
     branches:
       - release/*
       - main
-  # PR 이벤트 추가로 자동화 범위 확대
-  pull_request:
-    types: [opened, reopened, synchronize]
+  # pull_request_target: 포크 PR에서도 write 권한 유지
+  pull_request_target:
+    types: [opened, reopened, synchronize]
+    branches:
+      - develop
+      - main
+      - release/*

⚠️ pull_request_target 사용 시, 신뢰할 수 없는 코드를 checkout 후 빌드하는 스텝이 없어야 합니다. 현재 워크플로우는 checkout 단계가 없으므로 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release-drafter.yml around lines 3 - 10, Replace the
current pull_request trigger with pull_request_target and add a branches filter
to it (e.g., branches: [release/*, main]) so the workflow only runs for PRs
targeting those base branches and executes with write permissions; update the
workflow permissions to grant contents: write for the job that performs "Update
Draft Release" and confirm no step checks out or runs untrusted PR code (the
safety note references the absence of a checkout), keeping the triggers:
on.pull_request_target and the branches filter in sync.


permissions:
contents: write
pull-requests: read

jobs:
update_release_draft:
runs-on: ubuntu-latest
steps:
# Checkout 단계 제거 (불필요한 작업 생략)
- name: Update Draft Release
if: startsWith(github.ref, 'refs/heads/release/') || github.event_name == 'pull_request'
uses: release-drafter/release-drafter@v6
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if the workflow file exists and verify lines 23 and 31
fd -g "release-drafter.yml" -x cat -n {} | head -40

Repository: 2025-snowCode/snowCode-Client

Length of output: 55


🌐 Web query:

release-drafter v6 latest commit SHA github

💡 Result:

The release-drafter/release-drafter@v6 tag currently points to the same commit as the latest v6.x release (v6.2.0): 6db134d15f3909ccc9eefd369f02bd1e9cffdf97. [1][2]


🏁 Script executed:

# Find the workflow file
fd -name "release-drafter.yml" -type f

Repository: 2025-snowCode/snowCode-Client

Length of output: 303


🏁 Script executed:

# Also check the workflow directory structure
find . -path "./.git" -prune -o -type f -name "*.yml" -print | grep -i workflow | head -20

Repository: 2025-snowCode/snowCode-Client

Length of output: 301


🏁 Script executed:

# Read the workflow file to check lines 23 and 31
cat -n ./.github/workflows/release-drafter.yml | sed -n '20,35p'

Repository: 2025-snowCode/snowCode-Client

Length of output: 790


써드파티 액션을 가변 태그 @v6로 참조 중 — 커밋 SHA로 고정 필요

release-drafter/release-drafter@v6는 가변 태그로, 태그가 악의적인 커밋으로 이동될 경우 워크플로우가 의도하지 않은 코드를 실행할 수 있습니다. GitHub의 보안 강화 가이드에 따라 특정 커밋 SHA로 고정하세요.

🔒 개선 방법 (현재 v6 커밋 SHA로 고정)
-        uses: release-drafter/release-drafter@v6
+        uses: release-drafter/release-drafter@6db134d15f3909ccc9eefd369f02bd1e9cffdf97 # v6.2.0

23번 줄과 31번 줄 모두 동일하게 수정해주세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uses: release-drafter/release-drafter@v6
uses: release-drafter/release-drafter@6db134d15f3909ccc9eefd369f02bd1e9cffdf97 # v6.2.0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release-drafter.yml at line 23, The workflow references
the third-party action release-drafter/release-drafter@v6; replace this mutable
tag with a specific commit SHA to pin the action and prevent unexpected updates.
Locate every occurrence of the string "release-drafter/release-drafter@v6"
(there are two occurrences) and change each to
"release-drafter/release-drafter@<commit-sha>" using the exact commit SHA from
the v6 release you trust, making sure both occurrences use the same SHA.

with:
config-name: template/release-drafter.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish Release
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
uses: release-drafter/release-drafter@v6
with:
config-name: template/release-drafter.yml
publish: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3 changes: 0 additions & 3 deletions src/entities/course/model/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import type {Assignment} from '@/entities/assignment/model/types';
import type {ApiResponse, SemesterCode} from '@/shared/model/common';

export type {Assignment};
export type {SemesterCode, SubmissionStatus} from '@/shared/model/common';

/**
* 일정(Schedule) 인터페이스 정의
*/
Expand Down
2 changes: 1 addition & 1 deletion src/pages/course-overview/ui/AssignmentList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Badge from '@/shared/ui/Badge';
import {Link} from 'react-router-dom';
import type {Assignment} from '@/entities/course/model/types';
import type {Assignment} from '@/entities/assignment/model/types';

interface AssignmentListProps {
isOpen?: boolean;
Expand Down
3 changes: 2 additions & 1 deletion src/pages/course-overview/ui/CourseHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import snowcodeOverviewMini from '@/assets/images/snowcode_overview_mini.svg';
import {formatCourseTerm} from '@/shared/lib/course';
import CourseActionsBar from './CourseActionsBar';
import {useUserStore} from '@/entities/auth/model/useUserStore';
import type {CourseOverview, SemesterCode} from '@/entities/course/model/types';
import type {CourseOverview} from '@/entities/course/model/types';
import type {SemesterCode} from '@/shared/model/common';

interface CourseHeroProps {
courseData: Omit<CourseOverview, 'units'>;
Expand Down
5 changes: 4 additions & 1 deletion src/pages/select-assignment/AssignmentSelectPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import AssignmentListContainer from './ui/AssignmentListContainer';
import {useState} from 'react';
import {response, courseOptionsResponse} from '@/shared/mocks/assignmentSelectResponse';
import {
response,
courseOptionsResponse,
} from '@/shared/mocks/assignmentSelectResponse';
import {useCourseFilter} from '@/features/course/filter-course/lib/useCourseFilter';
import {AssignmentPageLayout} from '@/widgets/assignment-page-layout';
import SelectableItem from '@/shared/ui/SelectableItem';
Expand Down
3 changes: 2 additions & 1 deletion src/shared/lib/course.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {SemesterCode, Unit} from '@/entities/course/model/types';
import type {Unit} from '@/entities/course/model/types';
import type {SemesterCode} from '@/shared/model/common';

const SEMESTER_MAP: Record<SemesterCode, '1' | '2' | '여름' | '겨울'> = {
FIRST: '1',
Expand Down
6 changes: 4 additions & 2 deletions src/shared/ui/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const Badge = (props: BadgeProps) => {
);

// 인덱스 배지 (단원, 문제 등)
case 'index':
case 'index': {
const suffix = props.kind === 'unit' ? '단원' : ' 문제';

return (
Expand All @@ -93,9 +93,10 @@ const Badge = (props: BadgeProps) => {
{suffix}
</span>
);
}

// 제출 상태 배지
case 'submission':
case 'submission': {
const {label, icon} = SubmissionMeta[props.status!];

return (
Expand All @@ -106,6 +107,7 @@ const Badge = (props: BadgeProps) => {
{icon} {label}
</span>
);
}
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/shared/ui/SelectableItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {tv, type VariantProps} from 'tailwind-variants';

export const selectableItemStyles = tv({
const selectableItemStyles = tv({
base: 'cursor-pointer bg-background w-full flex items-center rounded-[9px] pl-4.5 pr-7.5 py-4 gap-4 border',
variants: {
selected: {
Expand Down
Loading