✨ 주요 개선사항: - any 타입 83개에서 62개로 21개 수정 (25% 감소) - 모든 ESLint 에러 11개 → 0개 완전 해결 - 타입 안전성 대폭 향상으로 런타임 오류 가능성 감소 🔧 수정된 파일들: • PWADebug.tsx - 사용하지 않는 import들에 _ prefix 추가 • categoryUtils.ts - 불필요한 any 캐스트 제거 • TransactionsHeader.tsx - BudgetData 인터페이스 정의 • storageUtils.ts - generic 타입과 unknown 타입 적용 • 각종 error handler들 - Error | {message?: string} 타입 적용 • test 파일들 - 적절한 mock 인터페이스 정의 • 유틸리티 파일들 - any → unknown 또는 적절한 타입으로 교체 🏆 성과: - 코드 품질 크게 향상 (280 → 80 문제로 71% 감소) - TypeScript 컴파일러의 타입 체크 효과성 증대 - 개발자 경험 개선 (IDE 자동완성, 타입 추론 등) 🧹 추가 정리: - ESLint no-console/no-alert 경고 해결 - Prettier 포맷팅 적용으로 코드 스타일 통일 🎯 다음 단계: 남은 62개 any 타입 계속 개선 예정 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
450 lines
11 KiB
JavaScript
450 lines
11 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
/**
|
||
* Linear GitHub Actions 워크플로우 테스터
|
||
* GitHub Actions 환경을 시뮬레이션하여 Linear 통합을 테스트
|
||
*/
|
||
|
||
const { execSync } = require("child_process");
|
||
const path = require("path");
|
||
|
||
// Simple command line argument parsing
|
||
const args = process.argv.slice(2);
|
||
const options = {
|
||
linearApiKey: null,
|
||
testType: "all",
|
||
issueId: "ZEL-1",
|
||
prUrl: "https://github.com/zellycloud/zellyy-finance/pull/1",
|
||
author: "hansoo",
|
||
verbose: args.includes("--verbose") || args.includes("-v"),
|
||
help: args.includes("--help") || args.includes("-h"),
|
||
};
|
||
|
||
// Extract options from arguments
|
||
const linearApiIndex = args.findIndex((arg) => arg.startsWith("--linear-api="));
|
||
if (linearApiIndex !== -1) {
|
||
options.linearApiKey = args[linearApiIndex].split("=")[1];
|
||
}
|
||
|
||
const testTypeIndex = args.findIndex((arg) => arg.startsWith("--test-type="));
|
||
if (testTypeIndex !== -1) {
|
||
options.testType = args[testTypeIndex].split("=")[1];
|
||
}
|
||
|
||
const issueIdIndex = args.findIndex((arg) => arg.startsWith("--issue-id="));
|
||
if (issueIdIndex !== -1) {
|
||
options.issueId = args[issueIdIndex].split("=")[1];
|
||
}
|
||
|
||
/**
|
||
* GitHub Actions 이벤트 시뮬레이션
|
||
*/
|
||
const mockEvents = {
|
||
pullRequest: {
|
||
opened: {
|
||
event: "pull_request",
|
||
action: "opened",
|
||
prUrl: options.prUrl,
|
||
prAuthor: options.author,
|
||
prMerged: "false",
|
||
},
|
||
closed: {
|
||
event: "pull_request",
|
||
action: "closed",
|
||
prUrl: options.prUrl,
|
||
prAuthor: options.author,
|
||
prMerged: "true",
|
||
},
|
||
review: {
|
||
event: "pull_request_review",
|
||
reviewState: "approved",
|
||
reviewer: "reviewer-user",
|
||
prUrl: options.prUrl,
|
||
},
|
||
},
|
||
push: {
|
||
commit: {
|
||
event: "push",
|
||
action: "commit",
|
||
commitSha: "abc123def456",
|
||
commitMessage: `feat: implement feature [${options.issueId}]`,
|
||
prAuthor: options.author,
|
||
},
|
||
},
|
||
issue: {
|
||
opened: {
|
||
event: "issue",
|
||
action: "opened",
|
||
githubIssueUrl: "https://github.com/zellycloud/zellyy-finance/issues/1",
|
||
},
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 스크립트 실행 함수
|
||
*/
|
||
async function runScript(scriptPath, args, description) {
|
||
console.log(`\n🔧 ${description}`);
|
||
console.log(` Script: ${scriptPath}`);
|
||
console.log(` Args: ${args.join(" ")}`);
|
||
|
||
try {
|
||
const command = `node ${scriptPath} ${args.join(" ")}`;
|
||
const result = execSync(command, {
|
||
encoding: "utf8",
|
||
cwd: process.cwd(),
|
||
env: {
|
||
...process.env,
|
||
LINEAR_API_KEY: options.linearApiKey,
|
||
},
|
||
});
|
||
|
||
if (options.verbose) {
|
||
console.log("📄 Output:");
|
||
console.log(result);
|
||
}
|
||
|
||
console.log("✅ Success");
|
||
return true;
|
||
} catch (error) {
|
||
console.error("❌ Failed:");
|
||
console.error(" Error:", error.message);
|
||
if (options.verbose && error.stdout) {
|
||
console.error(" Output:", error.stdout);
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Pull Request 이벤트 테스트
|
||
*/
|
||
async function testPullRequestFlow() {
|
||
console.log("\n🔀 Testing Pull Request Flow");
|
||
console.log("===============================");
|
||
|
||
const results = [];
|
||
|
||
// PR opened
|
||
console.log("\n📝 1. Pull Request Opened");
|
||
const openedEvent = mockEvents.pullRequest.opened;
|
||
|
||
// Sync PR status
|
||
let success = await runScript(
|
||
"scripts/linear-sync.cjs",
|
||
[
|
||
`--issue-id=${options.issueId}`,
|
||
`--event=${openedEvent.event}`,
|
||
`--action=${openedEvent.action}`,
|
||
`--pr-url=${openedEvent.prUrl}`,
|
||
`--pr-author=${openedEvent.prAuthor}`,
|
||
`--pr-merged=${openedEvent.prMerged}`,
|
||
],
|
||
"Sync PR opened status"
|
||
);
|
||
results.push({ test: "PR Sync (opened)", success });
|
||
|
||
// Add PR comment
|
||
success = await runScript(
|
||
"scripts/linear-comment.cjs",
|
||
[
|
||
`--issue-id=${options.issueId}`,
|
||
`--event=${openedEvent.event}`,
|
||
`--action=${openedEvent.action}`,
|
||
`--pr-url=${openedEvent.prUrl}`,
|
||
`--pr-author=${openedEvent.prAuthor}`,
|
||
],
|
||
"Add PR comment"
|
||
);
|
||
results.push({ test: "PR Comment (opened)", success });
|
||
|
||
// PR review
|
||
console.log("\n👁️ 2. Pull Request Review");
|
||
const reviewEvent = mockEvents.pullRequest.review;
|
||
|
||
success = await runScript(
|
||
"scripts/linear-comment.cjs",
|
||
[
|
||
`--issue-id=${options.issueId}`,
|
||
`--event=${reviewEvent.event}`,
|
||
`--review-state=${reviewEvent.reviewState}`,
|
||
`--reviewer=${reviewEvent.reviewer}`,
|
||
`--pr-url=${reviewEvent.prUrl}`,
|
||
],
|
||
"Add review comment"
|
||
);
|
||
results.push({ test: "PR Review Comment", success });
|
||
|
||
// PR merged
|
||
console.log("\n✅ 3. Pull Request Merged");
|
||
const closedEvent = mockEvents.pullRequest.closed;
|
||
|
||
success = await runScript(
|
||
"scripts/linear-sync.cjs",
|
||
[
|
||
`--issue-id=${options.issueId}`,
|
||
`--event=${closedEvent.event}`,
|
||
`--action=${closedEvent.action}`,
|
||
`--pr-url=${closedEvent.prUrl}`,
|
||
`--pr-author=${closedEvent.prAuthor}`,
|
||
`--pr-merged=${closedEvent.prMerged}`,
|
||
],
|
||
"Sync PR merged status"
|
||
);
|
||
results.push({ test: "PR Sync (merged)", success });
|
||
|
||
success = await runScript(
|
||
"scripts/linear-comment.cjs",
|
||
[
|
||
`--issue-id=${options.issueId}`,
|
||
`--event=${closedEvent.event}`,
|
||
`--action=${closedEvent.action}`,
|
||
`--pr-url=${closedEvent.prUrl}`,
|
||
`--pr-author=${closedEvent.prAuthor}`,
|
||
],
|
||
"Add merge comment"
|
||
);
|
||
results.push({ test: "PR Comment (merged)", success });
|
||
|
||
return results;
|
||
}
|
||
|
||
/**
|
||
* Push 이벤트 테스트
|
||
*/
|
||
async function testPushFlow() {
|
||
console.log("\n🚀 Testing Push Flow");
|
||
console.log("====================");
|
||
|
||
const results = [];
|
||
const pushEvent = mockEvents.push.commit;
|
||
|
||
// Sync commit
|
||
let success = await runScript(
|
||
"scripts/linear-sync.cjs",
|
||
[
|
||
`--issue-id=${options.issueId}`,
|
||
`--event=${pushEvent.event}`,
|
||
`--action=${pushEvent.action}`,
|
||
],
|
||
"Sync commit status"
|
||
);
|
||
results.push({ test: "Push Sync", success });
|
||
|
||
// Add commit comment
|
||
success = await runScript(
|
||
"scripts/linear-comment.cjs",
|
||
[
|
||
`--issue-id=${options.issueId}`,
|
||
`--event=${pushEvent.event}`,
|
||
`--commit-sha=${pushEvent.commitSha}`,
|
||
`--commit-message=${pushEvent.commitMessage}`,
|
||
`--pr-author=${pushEvent.prAuthor}`,
|
||
],
|
||
"Add commit comment"
|
||
);
|
||
results.push({ test: "Push Comment", success });
|
||
|
||
return results;
|
||
}
|
||
|
||
/**
|
||
* Issue 이벤트 테스트
|
||
*/
|
||
async function testIssueFlow() {
|
||
console.log("\n📋 Testing Issue Flow");
|
||
console.log("=====================");
|
||
|
||
const results = [];
|
||
const issueEvent = mockEvents.issue.opened;
|
||
|
||
// Add issue comment
|
||
const success = await runScript(
|
||
"scripts/linear-comment.cjs",
|
||
[
|
||
`--issue-id=${options.issueId}`,
|
||
`--event=${issueEvent.event}`,
|
||
`--action=${issueEvent.action}`,
|
||
`--github-issue-url=${issueEvent.githubIssueUrl}`,
|
||
],
|
||
"Add issue comment"
|
||
);
|
||
results.push({ test: "Issue Comment", success });
|
||
|
||
return results;
|
||
}
|
||
|
||
/**
|
||
* 릴리즈 플로우 테스트
|
||
*/
|
||
async function testReleaseFlow() {
|
||
console.log("\n🎯 Testing Release Flow");
|
||
console.log("=======================");
|
||
|
||
const results = [];
|
||
const version = "1.1.0";
|
||
|
||
// Release preparation
|
||
let success = await runScript(
|
||
"scripts/linear-release-prep.cjs",
|
||
[version],
|
||
"Prepare release"
|
||
);
|
||
results.push({ test: "Release Prep", success });
|
||
|
||
// Release completion
|
||
success = await runScript(
|
||
"scripts/linear-release-complete.cjs",
|
||
[version],
|
||
"Complete release"
|
||
);
|
||
results.push({ test: "Release Complete", success });
|
||
|
||
return results;
|
||
}
|
||
|
||
/**
|
||
* 전체 결과 요약
|
||
*/
|
||
function printSummary(allResults) {
|
||
console.log("\n📊 Test Results Summary");
|
||
console.log("========================");
|
||
|
||
let totalTests = 0;
|
||
let passedTests = 0;
|
||
|
||
allResults.forEach((flowResults) => {
|
||
flowResults.forEach((result) => {
|
||
totalTests++;
|
||
if (result.success) passedTests++;
|
||
|
||
const status = result.success ? "✅ PASS" : "❌ FAIL";
|
||
console.log(`${status} ${result.test}`);
|
||
});
|
||
});
|
||
|
||
console.log(`\n🎯 Overall: ${passedTests}/${totalTests} tests passed`);
|
||
|
||
if (passedTests === totalTests) {
|
||
console.log("🎉 All workflow tests passed! Linear integration is ready.");
|
||
} else {
|
||
console.log("⚠️ Some tests failed. Check the configuration and scripts.");
|
||
}
|
||
|
||
return passedTests === totalTests;
|
||
}
|
||
|
||
/**
|
||
* 사용법 출력
|
||
*/
|
||
function printUsage() {
|
||
console.log(`
|
||
🧪 Linear GitHub Actions 워크플로우 테스터
|
||
|
||
사용법:
|
||
node scripts/linear-workflow-tester.cjs [옵션]
|
||
|
||
옵션:
|
||
--linear-api=KEY Linear API 키 지정
|
||
--test-type=TYPE 테스트 타입 (all, pr, push, issue, release)
|
||
--issue-id=ID 테스트할 Linear 이슈 ID (기본: ZEL-1)
|
||
--verbose, -v 상세 출력
|
||
--help, -h 이 도움말 출력
|
||
|
||
예시:
|
||
# 전체 워크플로우 테스트
|
||
node scripts/linear-workflow-tester.cjs --linear-api=lin_api_xxx
|
||
|
||
# PR 플로우만 테스트
|
||
node scripts/linear-workflow-tester.cjs --test-type=pr --issue-id=ZEL-123
|
||
|
||
# 상세 출력과 함께 테스트
|
||
node scripts/linear-workflow-tester.cjs --linear-api=lin_api_xxx --verbose
|
||
|
||
테스트 타입:
|
||
- all: 모든 워크플로우 테스트 (기본값)
|
||
- pr: Pull Request 플로우만 테스트
|
||
- push: Push 이벤트 플로우만 테스트
|
||
- issue: Issue 이벤트 플로우만 테스트
|
||
- release: 릴리즈 플로우만 테스트
|
||
|
||
환경 변수:
|
||
LINEAR_API_KEY Linear API 키
|
||
`);
|
||
}
|
||
|
||
/**
|
||
* 메인 함수
|
||
*/
|
||
async function main() {
|
||
if (options.help) {
|
||
printUsage();
|
||
process.exit(0);
|
||
}
|
||
|
||
console.log("🧪 Linear GitHub Actions 워크플로우 테스터");
|
||
console.log("==========================================\n");
|
||
|
||
// API 키 확인
|
||
const apiKey = options.linearApiKey || process.env.LINEAR_API_KEY;
|
||
if (!apiKey) {
|
||
console.error(
|
||
"❌ Linear API 키가 필요합니다. --linear-api 옵션을 사용하거나 LINEAR_API_KEY 환경 변수를 설정하세요."
|
||
);
|
||
process.exit(1);
|
||
}
|
||
|
||
options.linearApiKey = apiKey;
|
||
|
||
console.log(`🔑 Linear API Key: ${apiKey.substring(0, 15)}...`);
|
||
console.log(`🏷️ Test Issue ID: ${options.issueId}`);
|
||
console.log(`📝 Test Type: ${options.testType}`);
|
||
|
||
const allResults = [];
|
||
|
||
try {
|
||
// 테스트 타입에 따라 실행
|
||
switch (options.testType) {
|
||
case "pr":
|
||
allResults.push(await testPullRequestFlow());
|
||
break;
|
||
case "push":
|
||
allResults.push(await testPushFlow());
|
||
break;
|
||
case "issue":
|
||
allResults.push(await testIssueFlow());
|
||
break;
|
||
case "release":
|
||
allResults.push(await testReleaseFlow());
|
||
break;
|
||
case "all":
|
||
default:
|
||
allResults.push(await testPullRequestFlow());
|
||
allResults.push(await testPushFlow());
|
||
allResults.push(await testIssueFlow());
|
||
allResults.push(await testReleaseFlow());
|
||
break;
|
||
}
|
||
|
||
// 결과 요약
|
||
const allPassed = printSummary(allResults);
|
||
|
||
process.exit(allPassed ? 0 : 1);
|
||
} catch (error) {
|
||
console.error("\n❌ 테스트 실행 중 오류 발생:", error.message);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
// 실행
|
||
if (require.main === module) {
|
||
main();
|
||
}
|
||
|
||
module.exports = {
|
||
testPullRequestFlow,
|
||
testPushFlow,
|
||
testIssueFlow,
|
||
testReleaseFlow,
|
||
};
|