feat: CI/CD 자동 배포 워크플로우 + 가이드 페이지 추가 #2

병합
htlee develop 에서 main 로 1 commits 를 머지했습니다 2026-02-14 20:06:05 +09:00
4개의 변경된 파일544개의 추가작업 그리고 0개의 파일을 삭제

파일 보기

@ -0,0 +1,36 @@
name: Build and Deploy
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Configure npm registry
run: |
echo "registry=https://nexus.gc-si.dev/repository/npm-public/" > .npmrc
echo "//nexus.gc-si.dev/repository/npm-public/:_auth=${{ secrets.NEXUS_NPM_AUTH }}" >> .npmrc
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to server
run: |
rm -rf /deploy/guide/*
cp -r dist/* /deploy/guide/
echo "Deployed at $(date '+%Y-%m-%d %H:%M:%S')"
ls -la /deploy/guide/

506
src/content/CiCdGuide.tsx Normal file
파일 보기

@ -0,0 +1,506 @@
import { Alert } from '../components/common/Alert';
import { CodeBlock } from '../components/common/CodeBlock';
import { StepGuide } from '../components/common/StepGuide';
export default function CiCdGuide() {
return (
<div className="max-w-4xl mx-auto py-12 px-6">
<h1 className="text-3xl font-bold text-text-primary mb-2">CI/CD </h1>
<p className="text-text-secondary mb-8">
Gitea Actions를 .
</p>
{/* 개요 */}
<h2 className="text-xl font-bold text-text-primary mt-10 mb-4"></h2>
<p className="text-text-secondary mb-4">
<strong>Gitea Actions</strong> GitHub Actions와 CI/CD .
, (push, MR )
.
</p>
<div className="bg-surface border border-border-default rounded-xl p-5 mb-6">
<div className="font-mono text-sm space-y-1.5 text-text-secondary">
<div className="flex items-center gap-2 flex-wrap">
<span className="text-accent font-semibold">main </span>
<span className="text-text-muted">&rarr;</span>
<span>Gitea Actions </span>
<span className="text-text-muted">&rarr;</span>
<span className="text-accent font-semibold">act_runner</span>
<span className="text-text-muted">&rarr;</span>
<span> + </span>
<span className="text-text-muted">&rarr;</span>
<span className="text-success font-semibold"> </span>
</div>
</div>
</div>
<div className="bg-surface border border-border-default rounded-xl p-5 mb-6">
<h3 className="font-semibold text-text-primary mb-3"> </h3>
<div className="font-mono text-sm space-y-1 text-text-secondary">
<p> &rarr; feature push &rarr; develop MR </p>
<p className="pl-8">&darr;</p>
<p className="pl-4">develop &rarr; main MR &rarr; &rarr; main </p>
<p className="pl-8">&darr;</p>
<p className="pl-4">
<span className="text-accent">Gitea Actions </span> &rarr; act_runner (Docker )
</p>
<p className="pl-8">&darr;</p>
<p className="pl-4">npm ci &rarr; npm run build &rarr; dist/ </p>
<p className="pl-8">&darr;</p>
<p className="pl-4">
&rarr; <span className="text-success">Nginx </span>
</p>
</div>
</div>
<Alert type="info">
GitHub Actions .
GitHub Actions .
</Alert>
{/* 워크플로우 파일 구조 */}
<h2 className="text-xl font-bold text-text-primary mt-10 mb-4"> </h2>
<p className="text-text-secondary mb-4">
{' '}
<code className="bg-bg-tertiary px-1 rounded">.gitea/workflows/</code> YAML .
</p>
<CodeBlock
language="bash"
code={`# 워크플로우 파일 위치
/
.gitea/
workflows/
deploy.yml `}
/>
<h3 className="text-lg font-semibold text-text-primary mt-6 mb-3"> </h3>
<CodeBlock
language="yaml"
code={`name: 워크플로우 이름
on: #
push:
branches:
- main # main push
jobs: #
job-name:
runs-on: ubuntu-latest # (Runner )
steps: #
- name: Step
uses: actions/checkout@v4 #
- name: 명령
run: echo "Hello" # `}
/>
{/* 트리거 조건 */}
<h2 className="text-xl font-bold text-text-primary mt-10 mb-4"> </h2>
<div className="overflow-x-auto">
<table className="w-full bg-surface border border-border-default rounded-lg overflow-hidden text-sm">
<thead>
<tr className="bg-bg-tertiary">
<th className="text-left px-4 py-3 font-semibold text-text-primary"></th>
<th className="text-left px-4 py-3 font-semibold text-text-primary"></th>
<th className="text-left px-4 py-3 font-semibold text-text-primary"></th>
</tr>
</thead>
<tbody className="divide-y divide-border-subtle">
<tr>
<td className="px-4 py-3 font-mono text-text-primary">push</td>
<td className="px-4 py-3 text-text-secondary"> push </td>
<td className="px-4 py-3 font-mono text-sm text-text-secondary">branches: [main]</td>
</tr>
<tr>
<td className="px-4 py-3 font-mono text-text-primary">pull_request</td>
<td className="px-4 py-3 text-text-secondary">MR / </td>
<td className="px-4 py-3 font-mono text-sm text-text-secondary">branches: [main], types: [opened, synchronize]</td>
</tr>
<tr>
<td className="px-4 py-3 font-mono text-text-primary">schedule</td>
<td className="px-4 py-3 text-text-secondary">Cron </td>
<td className="px-4 py-3 font-mono text-sm text-text-secondary">cron: &apos;0 2 * * *&apos;</td>
</tr>
<tr>
<td className="px-4 py-3 font-mono text-text-primary">workflow_dispatch</td>
<td className="px-4 py-3 text-text-secondary"> </td>
<td className="px-4 py-3 font-mono text-sm text-text-secondary">Gitea UI에서 </td>
</tr>
</tbody>
</table>
</div>
<Alert type="warning">
MR을 main에 <code className="bg-bg-tertiary px-1 rounded">push</code> .
<code className="bg-bg-tertiary px-1 rounded">on: push: branches: [main]</code> .
</Alert>
{/* 시크릿 관리 */}
<h2 className="text-xl font-bold text-text-primary mt-10 mb-4">릿 </h2>
<p className="text-text-secondary mb-4">
, 릿 .
{' '}
<code className="bg-bg-tertiary px-1 rounded">{'${{ secrets.시크릿_이름 }}'}</code> .
</p>
<StepGuide
steps={[
{
title: 'Gitea 리포지토리 페이지 접속',
content: (
<p>
&rarr; <strong>Settings</strong> &rarr; <strong>Actions</strong> &rarr;{' '}
<strong>Secrets</strong> .
</p>
),
},
{
title: '시크릿 추가',
content: (
<p>
<strong>Add Secret</strong> .
릿 <code className="bg-bg-tertiary px-1 rounded">UPPER_SNAKE_CASE</code> .
</p>
),
},
{
title: '워크플로우에서 사용',
content: (
<CodeBlock
language="yaml"
code={`- name: Configure registry
run: |
echo "//nexus.gc-si.dev/repository/npm-public/:_auth=\${{ secrets.NEXUS_NPM_AUTH }}" >> .npmrc`}
/>
),
},
]}
/>
<div className="overflow-x-auto mt-4">
<table className="w-full bg-surface border border-border-default rounded-lg overflow-hidden text-sm">
<thead>
<tr className="bg-bg-tertiary">
<th className="text-left px-4 py-3 font-semibold text-text-primary">릿 </th>
<th className="text-left px-4 py-3 font-semibold text-text-primary"></th>
<th className="text-left px-4 py-3 font-semibold text-text-primary"> </th>
</tr>
</thead>
<tbody className="divide-y divide-border-subtle">
<tr>
<td className="px-4 py-3 font-mono text-text-primary">NEXUS_NPM_AUTH</td>
<td className="px-4 py-3 text-text-secondary">Nexus npm (Base64)</td>
<td className="px-4 py-3 text-text-secondary">gc-guide</td>
</tr>
</tbody>
</table>
</div>
{/* gc-guide 워크플로우 예시 */}
<h2 className="text-xl font-bold text-text-primary mt-10 mb-4">gc-guide (React)</h2>
<p className="text-text-secondary mb-4">
gc-guide .
main .
</p>
<CodeBlock
language="yaml"
filename=".gitea/workflows/deploy.yml"
code={`name: Build and Deploy
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Configure npm registry
run: |
echo "registry=https://nexus.gc-si.dev/repository/npm-public/" > .npmrc
echo "//nexus.gc-si.dev/repository/npm-public/:_auth=\${{ secrets.NEXUS_NPM_AUTH }}" >> .npmrc
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to server
run: |
rm -rf /deploy/guide/*
cp -r dist/* /deploy/guide/
echo "Deployed at $(date '+%Y-%m-%d %H:%M:%S')"
ls -la /deploy/guide/`}
/>
<Alert type="info">
<code className="bg-bg-tertiary px-1 rounded">/deploy/guide/</code> act_runner
<code className="bg-bg-tertiary px-1 rounded">/devdata/services/guide/dist/</code>
. Nginx가 .
</Alert>
{/* gc-guide-api 워크플로우 예시 (향후) */}
<h2 className="text-xl font-bold text-text-primary mt-10 mb-4">gc-guide-api (Spring Boot, )</h2>
<p className="text-text-secondary mb-4">
Java/Spring Boot . gc-guide-api에 .
</p>
<CodeBlock
language="yaml"
filename=".gitea/workflows/deploy.yml (예정)"
code={`name: Build and Deploy API
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
container:
image: maven:3.9-eclipse-temurin-17
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure Maven (settings.xml)
run: |
mkdir -p ~/.m2
cat > ~/.m2/settings.xml << 'EOF'
<settings>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>https://nexus.gc-si.dev/repository/maven-public/</url>
</mirror>
</mirrors>
<servers>
<server>
<id>nexus</id>
<username>\${{ secrets.NEXUS_USERNAME }}</username>
<password>\${{ secrets.NEXUS_PASSWORD }}</password>
</server>
</servers>
</settings>
EOF
- name: Build
run: mvn clean package -DskipTests
- name: Deploy
run: |
cp target/*.jar /deploy/api/app.jar
# `}
/>
{/* Actions 확인 */}
<h2 className="text-xl font-bold text-text-primary mt-10 mb-4"> </h2>
<StepGuide
steps={[
{
title: 'Gitea 리포지토리 접속',
content: (
<p>
<a
href="https://gitea.gc-si.dev/gc/gc-guide"
target="_blank"
rel="noopener noreferrer"
className="text-accent hover:underline"
>
https://gitea.gc-si.dev/gc/gc-guide
</a>
.
</p>
),
},
{
title: 'Actions 탭 확인',
content: (
<p>
<strong>Actions</strong> .
.
</p>
),
},
{
title: '배포 결과 확인',
content: (
<p>
{' '}
<a
href="https://guide.gc-si.dev"
target="_blank"
rel="noopener noreferrer"
className="text-accent hover:underline"
>
https://guide.gc-si.dev
</a>
. (Ctrl+Shift+R).
</p>
),
},
]}
/>
{/* 워크플로우 작성 팁 */}
<h2 className="text-xl font-bold text-text-primary mt-10 mb-4"> </h2>
<h3 className="text-lg font-semibold text-text-primary mt-6 mb-3"> </h3>
<div className="overflow-x-auto">
<table className="w-full bg-surface border border-border-default rounded-lg overflow-hidden text-sm">
<thead>
<tr className="bg-bg-tertiary">
<th className="text-left px-4 py-3 font-semibold text-text-primary"></th>
<th className="text-left px-4 py-3 font-semibold text-text-primary"></th>
</tr>
</thead>
<tbody className="divide-y divide-border-subtle">
<tr>
<td className="px-4 py-3 font-mono text-text-primary">actions/checkout@v4</td>
<td className="px-4 py-3 text-text-secondary"> </td>
</tr>
<tr>
<td className="px-4 py-3 font-mono text-text-primary">actions/setup-node@v4</td>
<td className="px-4 py-3 text-text-secondary">Node.js </td>
</tr>
<tr>
<td className="px-4 py-3 font-mono text-text-primary">actions/setup-java@v4</td>
<td className="px-4 py-3 text-text-secondary">JDK </td>
</tr>
<tr>
<td className="px-4 py-3 font-mono text-text-primary">actions/cache@v4</td>
<td className="px-4 py-3 text-text-secondary"> ( )</td>
</tr>
</tbody>
</table>
</div>
<h3 className="text-lg font-semibold text-text-primary mt-6 mb-3"> 릿</h3>
<CodeBlock
language="yaml"
code={`# 워크플로우 레벨 환경변수
env:
NODE_ENV: production
# Step
steps:
- name: Build
env:
API_URL: https://api.gc-si.dev
run: npm run build
# 릿
steps:
- name: Deploy
run: echo "\${{ secrets.DEPLOY_TOKEN }}"`}
/>
{/* Runner 관리 (관리자) */}
<h2 className="text-xl font-bold text-text-primary mt-10 mb-4">Runner ()</h2>
<p className="text-text-secondary mb-4">
Gitea Actions는 <strong>act_runner</strong> .
Docker , job을 Docker .
</p>
<div className="overflow-x-auto mb-4">
<table className="w-full bg-surface border border-border-default rounded-lg overflow-hidden text-sm">
<thead>
<tr className="bg-bg-tertiary">
<th className="text-left px-4 py-3 font-semibold text-text-primary"></th>
<th className="text-left px-4 py-3 font-semibold text-text-primary"></th>
</tr>
</thead>
<tbody className="divide-y divide-border-subtle">
<tr>
<td className="px-4 py-3 text-text-primary">Runner </td>
<td className="px-4 py-3 font-mono text-text-secondary">gc-runner-01</td>
</tr>
<tr>
<td className="px-4 py-3 text-text-primary"></td>
<td className="px-4 py-3 font-mono text-text-secondary">act-runner (gitea/act_runner:latest)</td>
</tr>
<tr>
<td className="px-4 py-3 text-text-primary"></td>
<td className="px-4 py-3 font-mono text-text-secondary">ubuntu-latest, ubuntu-22.04</td>
</tr>
<tr>
<td className="px-4 py-3 text-text-primary"> </td>
<td className="px-4 py-3 font-mono text-text-secondary">node:20 (Docker)</td>
</tr>
<tr>
<td className="px-4 py-3 text-text-primary"></td>
<td className="px-4 py-3 font-mono text-text-secondary">devnet (Gitea/Nexus와 )</td>
</tr>
</tbody>
</table>
</div>
<h3 className="text-lg font-semibold text-text-primary mt-6 mb-3"> </h3>
<CodeBlock
language="bash"
code={`# Runner 상태 확인
docker ps | grep act-runner
docker logs act-runner --tail 20
# Runner
cd /devdata/services && docker compose restart act-runner
# Runner
# Gitea Site Administration Runners
#
cd /devdata/services && docker compose down && docker compose up -d`}
/>
{/* 트러블슈팅 */}
<h2 className="text-xl font-bold text-text-primary mt-10 mb-4"></h2>
<div className="space-y-4">
<div className="bg-surface border border-border-default rounded-xl p-5">
<h4 className="font-semibold text-text-primary mb-2"> </h4>
<ul className="list-disc list-inside text-text-secondary space-y-1 text-sm">
<li> Settings &rarr; Actions가 </li>
<li> : <code className="bg-bg-tertiary px-1 rounded">.gitea/workflows/*.yml</code></li>
<li>Runner가 Online 확인: Gitea &rarr; Runners</li>
<li><code className="bg-bg-tertiary px-1 rounded">runs-on</code> Runner </li>
</ul>
</div>
<div className="bg-surface border border-border-default rounded-xl p-5">
<h4 className="font-semibold text-text-primary mb-2">npm ci </h4>
<ul className="list-disc list-inside text-text-secondary space-y-1 text-sm">
<li>NEXUS_NPM_AUTH 릿 </li>
<li>Nexus (act_runner가 devnet )</li>
<li><code className="bg-bg-tertiary px-1 rounded">package-lock.json</code> (npm ci )</li>
</ul>
</div>
<div className="bg-surface border border-border-default rounded-xl p-5">
<h4 className="font-semibold text-text-primary mb-2"> </h4>
<ul className="list-disc list-inside text-text-secondary space-y-1 text-sm">
<li> : <kbd className="bg-bg-tertiary px-1.5 py-0.5 rounded text-xs">Ctrl+Shift+R</kbd></li>
<li>Actions Deploy </li>
<li> : <code className="bg-bg-tertiary px-1 rounded">ls -la /devdata/services/guide/dist/</code></li>
</ul>
</div>
<div className="bg-surface border border-border-default rounded-xl p-5">
<h4 className="font-semibold text-text-primary mb-2">Runner가 Offline으로 </h4>
<ul className="list-disc list-inside text-text-secondary space-y-1 text-sm">
<li> : <code className="bg-bg-tertiary px-1 rounded">docker ps | grep act-runner</code></li>
<li> : <code className="bg-bg-tertiary px-1 rounded">docker logs act-runner --tail 30</code></li>
<li> : <code className="bg-bg-tertiary px-1 rounded">docker compose restart act-runner</code></li>
</ul>
</div>
</div>
</div>
);
}

파일 보기

@ -10,6 +10,7 @@ const CONTENT_MAP: Record<string, React.LazyExoticComponent<React.ComponentType>
'chat-bot': lazy(() => import('../content/ChatBotIntegration')),
'starting-project': lazy(() => import('../content/StartingProject')),
'design-system': lazy(() => import('../content/DesignSystem')),
'ci-cd': lazy(() => import('../content/CiCdGuide')),
};
export function GuidePage() {

파일 보기

@ -9,6 +9,7 @@ export const DEV_NAV: NavItem[] = [
{ path: '/dev/chat-bot', label: 'Chat 봇 연동' },
{ path: '/dev/starting-project', label: '프로젝트 시작하기' },
{ path: '/dev/design-system', label: '디자인 시스템' },
{ path: '/dev/ci-cd', label: 'CI/CD 자동 배포' },
];
export const ADMIN_NAV: NavItem[] = [