mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-05 12:00:59 +08:00
feat: implement hybrid database architecture and frontend encryption
- Add PostgreSQL + SQLite hybrid database support with automatic switching - Implement frontend AES-GCM + RSA-OAEP encryption for sensitive data - Add comprehensive DatabaseInterface with all required methods - Fix compilation issues with interface consistency - Update all database method signatures to use DatabaseInterface - Add missing UpdateTraderInitialBalance method to PostgreSQL implementation - Integrate RSA public key distribution via /api/config endpoint - Add frontend crypto service with proper error handling - Support graceful degradation between encrypted and plaintext transmission - Add directory creation for RSA keys and PEM parsing fixes - Test both SQLite and PostgreSQL modes successfully 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
95
.github/workflows/docker-build.yml
vendored
Normal file
95
.github/workflows/docker-build.yml
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
name: Build and Push Docker Images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY_GHCR: ghcr.io
|
||||
IMAGE_NAME_BACKEND: ${{ github.repository }}/nofx-backend
|
||||
IMAGE_NAME_FRONTEND: ${{ github.repository }}/nofx-frontend
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: backend
|
||||
dockerfile: ./docker/Dockerfile.backend
|
||||
image_suffix: backend
|
||||
- name: frontend
|
||||
dockerfile: ./docker/Dockerfile.frontend
|
||||
image_suffix: frontend
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY_GHCR }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
continue-on-error: true
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY_GHCR }}/${{ github.repository }}/nofx-${{ matrix.image_suffix }}
|
||||
${{ secrets.DOCKERHUB_USERNAME && format('{0}/nofx-{1}', secrets.DOCKERHUB_USERNAME, matrix.image_suffix) || '' }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha,prefix={{branch}}
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push ${{ matrix.name }} image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ${{ matrix.dockerfile }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
build-args: |
|
||||
BUILD_DATE=${{ github.event.head_commit.timestamp }}
|
||||
VCS_REF=${{ github.sha }}
|
||||
VERSION=${{ github.ref_name }}
|
||||
|
||||
- name: Image digest
|
||||
run: echo "Image digest for ${{ matrix.name }} - ${{ steps.meta.outputs.digest }}"
|
||||
176
.github/workflows/pr-checks-comment.yml
vendored
176
.github/workflows/pr-checks-comment.yml
vendored
@@ -104,6 +104,53 @@ jobs:
|
||||
echo "⚠️ Frontend results artifact not found"
|
||||
fi
|
||||
|
||||
- name: Get PR information
|
||||
id: pr-info
|
||||
if: steps.backend.outputs.pr_number != '0'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const prNumber = ${{ steps.backend.outputs.pr_number }};
|
||||
|
||||
// Get PR details
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber
|
||||
});
|
||||
|
||||
// Check PR title format (Conventional Commits)
|
||||
const prTitle = pr.title;
|
||||
const conventionalCommitPattern = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|security|build)(\(.+\))?: .+/;
|
||||
const titleValid = conventionalCommitPattern.test(prTitle);
|
||||
|
||||
core.setOutput('pr_title', prTitle);
|
||||
core.setOutput('title_valid', titleValid);
|
||||
|
||||
// Calculate PR size
|
||||
const additions = pr.additions;
|
||||
const deletions = pr.deletions;
|
||||
const total = additions + deletions;
|
||||
|
||||
let size = '';
|
||||
let sizeEmoji = '';
|
||||
if (total < 300) {
|
||||
size = 'Small';
|
||||
sizeEmoji = '🟢';
|
||||
} else if (total < 1000) {
|
||||
size = 'Medium';
|
||||
sizeEmoji = '🟡';
|
||||
} else {
|
||||
size = 'Large';
|
||||
sizeEmoji = '🔴';
|
||||
}
|
||||
|
||||
core.setOutput('pr_size', size);
|
||||
core.setOutput('size_emoji', sizeEmoji);
|
||||
core.setOutput('total_lines', total);
|
||||
core.setOutput('additions', additions);
|
||||
core.setOutput('deletions', deletions);
|
||||
|
||||
- name: Post advisory results comment
|
||||
if: steps.backend.outputs.pr_number != '0'
|
||||
uses: actions/github-script@v7
|
||||
@@ -113,7 +160,40 @@ jobs:
|
||||
|
||||
let comment = '## 🤖 Advisory Check Results\n\n';
|
||||
comment += 'These are **advisory** checks to help improve code quality. They won\'t block your PR from being merged.\n\n';
|
||||
comment += '> **Note:** PR title and size checks are handled by the main workflow and may appear in a separate comment.\n\n';
|
||||
|
||||
// PR Information section
|
||||
const prTitle = '${{ steps.pr-info.outputs.pr_title }}';
|
||||
const titleValid = '${{ steps.pr-info.outputs.title_valid }}' === 'true';
|
||||
const prSize = '${{ steps.pr-info.outputs.pr_size }}';
|
||||
const sizeEmoji = '${{ steps.pr-info.outputs.size_emoji }}';
|
||||
const totalLines = '${{ steps.pr-info.outputs.total_lines }}';
|
||||
const additions = '${{ steps.pr-info.outputs.additions }}';
|
||||
const deletions = '${{ steps.pr-info.outputs.deletions }}';
|
||||
|
||||
comment += '### 📋 PR Information\n\n';
|
||||
|
||||
// Title check
|
||||
if (titleValid) {
|
||||
comment += '**Title Format:** ✅ Good - Follows Conventional Commits\n';
|
||||
} else {
|
||||
comment += '**Title Format:** ⚠️ Suggestion - Consider using `type(scope): description`\n';
|
||||
comment += '<details><summary>Recommended format</summary>\n\n';
|
||||
comment += '**Valid types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`, `security`, `build`\n\n';
|
||||
comment += '**Examples:**\n';
|
||||
comment += '- `feat(trader): add new trading strategy`\n';
|
||||
comment += '- `fix(api): resolve authentication issue`\n';
|
||||
comment += '- `docs: update README`\n';
|
||||
comment += '</details>\n\n';
|
||||
}
|
||||
|
||||
// Size check
|
||||
comment += `**PR Size:** ${sizeEmoji} ${prSize} (${totalLines} lines: +${additions} -${deletions})\n`;
|
||||
|
||||
if (prSize === 'Large') {
|
||||
comment += '\n💡 **Suggestion:** This is a large PR. Consider breaking it into smaller, focused PRs for easier review.\n';
|
||||
}
|
||||
|
||||
comment += '\n';
|
||||
|
||||
// Backend checks
|
||||
const fmtStatus = '${{ steps.backend.outputs.fmt_status }}';
|
||||
@@ -208,37 +288,71 @@ jobs:
|
||||
return;
|
||||
}
|
||||
|
||||
const prNumber = pulls.data[0].number;
|
||||
const pr = pulls.data[0];
|
||||
const prNumber = pr.number;
|
||||
|
||||
const comment = [
|
||||
'## ⚠️ Advisory Checks - Results Unavailable',
|
||||
'',
|
||||
'The advisory checks workflow completed, but results could not be retrieved.',
|
||||
'',
|
||||
'### Possible reasons:',
|
||||
'- Artifacts were not uploaded successfully',
|
||||
'- Artifacts expired (retention: 1 day)',
|
||||
'- Permission issues',
|
||||
'',
|
||||
'### What to do:',
|
||||
'1. Check the [PR Checks - Run workflow](${{ github.event.workflow_run.html_url }}) logs',
|
||||
'2. Ensure your code passes local checks:',
|
||||
'```bash',
|
||||
'# Backend',
|
||||
'go fmt ./...',
|
||||
'go vet ./...',
|
||||
'go build',
|
||||
'go test ./...',
|
||||
'',
|
||||
'# Frontend (if applicable)',
|
||||
'cd web',
|
||||
'npm run build',
|
||||
'```',
|
||||
'',
|
||||
'---',
|
||||
'',
|
||||
'*This is an automated fallback message. The advisory checks ran but results are not available.*'
|
||||
].join('\n');
|
||||
// Get PR information for fallback comment
|
||||
const prTitle = pr.title;
|
||||
const conventionalCommitPattern = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|security|build)(\(.+\))?: .+/;
|
||||
const titleValid = conventionalCommitPattern.test(prTitle);
|
||||
|
||||
const additions = pr.additions || 0;
|
||||
const deletions = pr.deletions || 0;
|
||||
const total = additions + deletions;
|
||||
|
||||
let size = '';
|
||||
let sizeEmoji = '';
|
||||
if (total < 300) {
|
||||
size = 'Small';
|
||||
sizeEmoji = '🟢';
|
||||
} else if (total < 1000) {
|
||||
size = 'Medium';
|
||||
sizeEmoji = '🟡';
|
||||
} else {
|
||||
size = 'Large';
|
||||
sizeEmoji = '🔴';
|
||||
}
|
||||
|
||||
let comment = '## ⚠️ Advisory Checks - Results Unavailable\n\n';
|
||||
comment += 'The advisory checks workflow completed, but results could not be retrieved.\n\n';
|
||||
|
||||
// Add PR Information
|
||||
comment += '### 📋 PR Information\n\n';
|
||||
|
||||
if (titleValid) {
|
||||
comment += '**Title Format:** ✅ Good - Follows Conventional Commits\n';
|
||||
} else {
|
||||
comment += '**Title Format:** ⚠️ Suggestion - Consider using `type(scope): description`\n';
|
||||
}
|
||||
|
||||
comment += `**PR Size:** ${sizeEmoji} ${size} (${total} lines: +${additions} -${deletions})\n\n`;
|
||||
|
||||
if (size === 'Large') {
|
||||
comment += '💡 **Suggestion:** This is a large PR. Consider breaking it into smaller, focused PRs for easier review.\n\n';
|
||||
}
|
||||
|
||||
comment += '---\n\n';
|
||||
comment += '### ⚠️ Backend/Frontend Check Results\n\n';
|
||||
comment += 'Results could not be retrieved.\n\n';
|
||||
comment += '**Possible reasons:**\n';
|
||||
comment += '- Artifacts were not uploaded successfully\n';
|
||||
comment += '- Artifacts expired (retention: 1 day)\n';
|
||||
comment += '- Permission issues\n\n';
|
||||
comment += '**What to do:**\n';
|
||||
comment += `1. Check the [PR Checks - Run workflow](${context.payload.workflow_run?.html_url || 'logs'}) logs\n`;
|
||||
comment += '2. Ensure your code passes local checks:\n';
|
||||
comment += '```bash\n';
|
||||
comment += '# Backend\n';
|
||||
comment += 'go fmt ./...\n';
|
||||
comment += 'go vet ./...\n';
|
||||
comment += 'go build\n';
|
||||
comment += 'go test ./...\n\n';
|
||||
comment += '# Frontend (if applicable)\n';
|
||||
comment += 'cd web\n';
|
||||
comment += 'npm run build\n';
|
||||
comment += '```\n\n';
|
||||
comment += '---\n\n';
|
||||
comment += '*This is an automated fallback message. The advisory checks ran but results are not available.*';
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: prNumber,
|
||||
|
||||
189
.github/workflows/pr-template-suggester.yml
vendored
Normal file
189
.github/workflows/pr-template-suggester.yml
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
name: PR Template Suggester
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
suggest-template:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Analyze PR files and auto-apply template
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { data: files } = await github.rest.pulls.listFiles({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number,
|
||||
});
|
||||
|
||||
let goFiles = 0, jsFiles = 0, tsFiles = 0, mdFiles = 0, otherFiles = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const filename = file.filename.toLowerCase();
|
||||
if (filename.endsWith('.go')) goFiles++;
|
||||
else if (filename.endsWith('.js') || filename.endsWith('.jsx')) jsFiles++;
|
||||
else if (filename.endsWith('.ts') || filename.endsWith('.tsx') || filename.endsWith('.vue')) tsFiles++;
|
||||
else if (filename.endsWith('.md')) mdFiles++;
|
||||
else otherFiles++;
|
||||
}
|
||||
|
||||
const totalFiles = goFiles + jsFiles + tsFiles + mdFiles + otherFiles;
|
||||
if (totalFiles === 0) { console.log('No files changed'); return; }
|
||||
|
||||
let suggestedTemplate = null, templateEmoji = '', templateLabel = '';
|
||||
|
||||
if (goFiles / totalFiles > 0.5) {
|
||||
suggestedTemplate = 'backend'; templateEmoji = '🔧'; templateLabel = 'backend';
|
||||
} else if ((jsFiles + tsFiles) / totalFiles > 0.5) {
|
||||
suggestedTemplate = 'frontend'; templateEmoji = '🎨'; templateLabel = 'frontend';
|
||||
} else if (mdFiles / totalFiles > 0.7) {
|
||||
suggestedTemplate = 'docs'; templateEmoji = '📝'; templateLabel = 'documentation';
|
||||
}
|
||||
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number,
|
||||
});
|
||||
|
||||
const prBody = pr.body || '';
|
||||
const usesBackendTemplate = prBody.includes('Pull Request - Backend');
|
||||
const usesFrontendTemplate = prBody.includes('Pull Request - Frontend');
|
||||
const usesDocsTemplate = prBody.includes('Pull Request - Documentation');
|
||||
const usesGeneralTemplate = prBody.includes('Pull Request - General');
|
||||
const usingDefaultTemplate = !usesBackendTemplate && !usesFrontendTemplate && !usesDocsTemplate && !usesGeneralTemplate;
|
||||
|
||||
if (templateLabel) {
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
labels: [templateLabel]
|
||||
});
|
||||
console.log('Added label: ' + templateLabel);
|
||||
} catch (error) {
|
||||
console.log('Label might not exist, skipping...');
|
||||
}
|
||||
}
|
||||
|
||||
function isPRBodyEmpty(body) {
|
||||
if (!body || body.trim().length < 100) return true;
|
||||
const hasEmptyDescription = body.includes('**English:**') && body.match(/\*\*English:\*\*\s*\n\s*\n\s*\n/);
|
||||
const hasEmptyChanges = body.includes('具体变更') && body.match(/\*\*中文:\*\*\s*\n\s*-\s*\n\s*-\s*\n/);
|
||||
if (hasEmptyDescription || hasEmptyChanges) return true;
|
||||
const descMatch = body.match(/\*\*English:\*\*[||]\s*\*\*中文:\*\*\s*\n\s*(.+)/);
|
||||
if (!descMatch || descMatch[1].trim().length < 10) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (suggestedTemplate && usingDefaultTemplate) {
|
||||
const shouldAutoApply = isPRBodyEmpty(prBody);
|
||||
const templatePath = '.github/PULL_REQUEST_TEMPLATE/' + suggestedTemplate + '.md';
|
||||
|
||||
if (shouldAutoApply) {
|
||||
try {
|
||||
const { data: templateFile } = await github.rest.repos.getContent({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
path: templatePath,
|
||||
ref: context.payload.pull_request.head.ref
|
||||
});
|
||||
|
||||
const templateContent = Buffer.from(templateFile.content, 'base64').toString('utf-8');
|
||||
|
||||
await github.rest.pulls.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number,
|
||||
body: templateContent
|
||||
});
|
||||
|
||||
console.log('Auto-applied ' + suggestedTemplate + ' template');
|
||||
|
||||
let fileStats = [];
|
||||
if (goFiles > 0) fileStats.push('- 🔧 Go files: ' + goFiles);
|
||||
if (jsFiles > 0) fileStats.push('- 🎨 JavaScript files: ' + jsFiles);
|
||||
if (tsFiles > 0) fileStats.push('- 🎨 TypeScript files: ' + tsFiles);
|
||||
if (mdFiles > 0) fileStats.push('- 📝 Markdown files: ' + mdFiles);
|
||||
if (otherFiles > 0) fileStats.push('- 📦 Other files: ' + otherFiles);
|
||||
const fileStatsText = fileStats.join('\n');
|
||||
|
||||
const notifyComment = '## ' + templateEmoji + ' 已自动应用专用模板 | Auto-Applied Template\n\n' +
|
||||
'检测到您的PR主要包含 **' + suggestedTemplate + '** 相关的变更,系统已自动为您应用相应的模板。\n\n' +
|
||||
'Detected that your PR primarily contains **' + suggestedTemplate + '** changes. The appropriate template has been automatically applied.\n\n' +
|
||||
'**文件统计 | File Statistics**\n' + fileStatsText + '\n\n' +
|
||||
'**已应用模板 | Applied Template**\n`' + templatePath + '`\n\n' +
|
||||
'✨ 您现在可以直接在PR描述中填写相关信息了!\n\n' +
|
||||
'✨ You can now fill in the relevant information in the PR description!';
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: notifyComment
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.log('Failed to fetch or apply template: ' + error.message);
|
||||
const templateUrl = 'https://raw.githubusercontent.com/' + context.repo.owner + '/' + context.repo.repo + '/dev/.github/PULL_REQUEST_TEMPLATE/' + suggestedTemplate + '.md';
|
||||
const fallbackComment = '## ' + templateEmoji + ' 建议使用专用模板 | Suggested Template\n\n' +
|
||||
'您的PR主要包含 **' + suggestedTemplate + '** 相关的变更。\n\n' +
|
||||
'**推荐模板 | Recommended Template:** `.github/PULL_REQUEST_TEMPLATE/' + suggestedTemplate + '.md`\n\n' +
|
||||
'**如何使用 | How to use:** [点击查看模板内容](' + templateUrl + ')';
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: fallbackComment
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log('PR body has content, sending suggestion only');
|
||||
|
||||
let fileStats = [];
|
||||
if (goFiles > 0) fileStats.push('- 🔧 Go files: ' + goFiles);
|
||||
if (jsFiles > 0) fileStats.push('- 🎨 JavaScript files: ' + jsFiles);
|
||||
if (tsFiles > 0) fileStats.push('- 🎨 TypeScript files: ' + tsFiles);
|
||||
if (mdFiles > 0) fileStats.push('- 📝 Markdown files: ' + mdFiles);
|
||||
if (otherFiles > 0) fileStats.push('- 📦 Other files: ' + otherFiles);
|
||||
const fileStatsText = fileStats.join('\n');
|
||||
|
||||
const templateUrl = 'https://raw.githubusercontent.com/' + context.repo.owner + '/' + context.repo.repo + '/dev/.github/PULL_REQUEST_TEMPLATE/' + suggestedTemplate + '.md';
|
||||
|
||||
const comment = '## ' + templateEmoji + ' 建议使用专用模板 | Suggested Template\n\n' +
|
||||
'您的PR主要包含 **' + suggestedTemplate + '** 相关的变更。我们建议使用更适合的模板以简化填写。\n\n' +
|
||||
'Your PR primarily contains **' + suggestedTemplate + '** changes. We suggest using a more suitable template to simplify filling.\n\n' +
|
||||
'**文件统计 | File Statistics**\n' + fileStatsText + '\n\n' +
|
||||
'**推荐模板 | Recommended Template**\n```\n.github/PULL_REQUEST_TEMPLATE/' + suggestedTemplate + '.md\n```\n\n' +
|
||||
'**如何使用 | How to use**\n' +
|
||||
'1. 编辑PR描述 | Edit PR description\n' +
|
||||
'2. 复制 [' + suggestedTemplate + ' 模板内容](' + templateUrl + ') | Copy [' + suggestedTemplate + ' template content](' + templateUrl + ')\n' +
|
||||
'3. 或在创建PR时使用URL参数 | Or use URL parameter when creating PR\n' +
|
||||
' `?template=' + suggestedTemplate + '.md`\n\n' +
|
||||
'_这是一个自动建议,您可以继续使用当前模板。_\n\n' +
|
||||
'_This is an automated suggestion. You may continue using the current template._';
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: comment
|
||||
});
|
||||
}
|
||||
} else if (suggestedTemplate && !usingDefaultTemplate) {
|
||||
console.log('PR already uses a specific template');
|
||||
} else {
|
||||
console.log('No specific template suggestion needed - mixed changes');
|
||||
}
|
||||
Reference in New Issue
Block a user