Files
zellyy-finance/scripts/linear-workflow-tester.cjs
hansoo 8343b25439 feat: Stage 2 TypeScript 타입 안전성 개선 - any 타입 83개 → 62개 대폭 감소
 주요 개선사항:
- 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>
2025-07-14 10:08:51 +09:00

450 lines
11 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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,
};