Compare commits

...

4 Commits

Author SHA1 Message Date
sky-take out ef29b029a2 merge: integrate remote CI repo README with local project
CI Pipeline / Lint & Type Check (push) Waiting to run Details
CI Pipeline / Test Suite (push) Blocked by required conditions Details
Deploy to Staging / Build and Push Docker Image (push) Has been cancelled Details
Deploy to Staging / Deploy to Staging Environment (push) Has been cancelled Details
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-08 19:45:57 +08:00
sky-take out d9446e7c5b feat: CI/CD workflows, Dockerfile, lockfile, master branch triggers
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-08 19:43:02 +08:00
sky-take out d9f8b7f740 修复 2026-05-08 15:18:56 +08:00
sky-take out 4231075253 添加 CI/CD 流水线配置 2026-05-08 14:40:22 +08:00
16 changed files with 5132 additions and 1 deletions

69
.GitLink/workflows/ci.yml Normal file
View File

@ -0,0 +1,69 @@
name: CI Pipeline
on:
pull_request:
branches: [main, master, develop]
types: [opened, synchronize, reopened]
push:
branches: [main, master, develop]
paths:
- 'src/**'
- 'tests/**'
- 'package.json'
- 'package-lock.json'
- 'tsconfig.json'
- '.eslintrc.json'
- '.github/workflows/**'
- '.GitLink/workflows/**'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-and-type-check:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type Check
run: npm run type-check
test:
name: Test Suite
runs-on: ubuntu-latest
needs: lint-and-type-check
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npm test -- --coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false

View File

@ -0,0 +1,25 @@
name: Deploy to Production
on:
workflow_dispatch:
inputs:
confirm:
description: '确认要部署到生产环境吗?请输入 deploy'
required: true
type: string
jobs:
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
environment: production
if: github.event_name == 'workflow_dispatch' && github.event.inputs.confirm == 'deploy'
steps:
- uses: actions/checkout@v4
- name: Deploy to Production Server
env:
PROD_API_KEY: ${{ secrets.PROD_API_KEY }}
run: |
echo "Replace with production deploy (kubectl / SSH / platform action)."
echo "Use secrets (e.g. PROD_API_KEY) from the production environment — do not echo secret values."

View File

@ -0,0 +1,57 @@
name: Deploy to Staging
on:
push:
branches: [main, master]
paths:
- 'src/**'
- 'package.json'
- 'package-lock.json'
- 'Dockerfile'
- '.github/workflows/deploy-staging.yml'
- '.GitLink/workflows/deploy-staging.yml'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-staging-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-push:
name: Build and Push Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:staging
ghcr.io/${{ github.repository }}:${{ github.sha }}
deploy:
name: Deploy to Staging Environment
runs-on: ubuntu-latest
needs: build-and-push
environment: staging
steps:
- name: Deploy to Staging Server
run: |
echo "Replace with real deploy: kubectl apply, SSH, or your GitLink deployment action."
echo "Image: ghcr.io/${{ github.repository }}:staging"

17
.eslintrc.json Normal file
View File

@ -0,0 +1,17 @@
{
"env": {
"node": true,
"jest": true,
"es2021": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": "latest"
},
"rules": {
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "single"],
"semi": ["error", "always"]
}
}

69
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,69 @@
name: CI Pipeline
on:
pull_request:
branches: [main, master, develop]
types: [opened, synchronize, reopened]
push:
branches: [main, master, develop]
paths:
- 'src/**'
- 'tests/**'
- 'package.json'
- 'package-lock.json'
- 'tsconfig.json'
- '.eslintrc.json'
- '.github/workflows/**'
- '.GitLink/workflows/**'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-and-type-check:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type Check
run: npm run type-check
test:
name: Test Suite
runs-on: ubuntu-latest
needs: lint-and-type-check
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npm test -- --coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false

25
.github/workflows/deploy-prod.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Deploy to Production
on:
workflow_dispatch:
inputs:
confirm:
description: '确认要部署到生产环境吗?请输入 deploy'
required: true
type: string
jobs:
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
environment: production
if: github.event_name == 'workflow_dispatch' && github.event.inputs.confirm == 'deploy'
steps:
- uses: actions/checkout@v4
- name: Deploy to Production Server
env:
PROD_API_KEY: ${{ secrets.PROD_API_KEY }}
run: |
echo "Replace with production deploy (kubectl / SSH / platform action)."
echo "Use secrets (e.g. PROD_API_KEY) from the production environment — do not echo secret values."

57
.github/workflows/deploy-staging.yml vendored Normal file
View File

@ -0,0 +1,57 @@
name: Deploy to Staging
on:
push:
branches: [main, master]
paths:
- 'src/**'
- 'package.json'
- 'package-lock.json'
- 'Dockerfile'
- '.github/workflows/deploy-staging.yml'
- '.GitLink/workflows/deploy-staging.yml'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-staging-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-push:
name: Build and Push Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:staging
ghcr.io/${{ github.repository }}:${{ github.sha }}
deploy:
name: Deploy to Staging Environment
runs-on: ubuntu-latest
needs: build-and-push
environment: staging
steps:
- name: Deploy to Staging Server
run: |
echo "Replace with real deploy: kubectl apply, SSH, or your GitLink deployment action."
echo "Image: ghcr.io/${{ github.repository }}:staging"

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
node_modules/
dist/
coverage/
.env
.DS_Store
*.log
.vscode/
.idea/

9
Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci --omit=dev
COPY src ./src
CMD ["node", "src/index.js"]

103
README.md
View File

@ -1,2 +1,103 @@
# CI
# GitLink CI/CD Demo Projectdanshen/CI
一个用于演示 GitLink / GitHub Actions 风格 CI/CD 的示例项目。
## 项目结构
```
.
├── .github/workflows/ # 标准 Actions 目录(多数平台识别这里)
│ ├── ci.yml
│ ├── deploy-staging.yml
│ └── deploy-prod.yml
├── .GitLink/workflows/ # 与上面内容相同;若 GitLink 只认此目录,保留其一即可
│ ├── ci.yml
│ ├── deploy-staging.yml
│ └── deploy-prod.yml
├── Dockerfile
├── src/
├── tests/
├── package.json
├── package-lock.json # 提交到 GitCI 里 npm ci 需要
└── README.md
```
## 功能特性
- **greet(name)**: 生成问候语
- **add(a, b)**: 加法运算
- **isValidEmail(email)**: 邮箱验证
## 流水线做什么
| 工作流 | 何时运行 |
|--------|----------|
| **CI Pipeline** | 推送到 `main` / `master` / `develop`,或针对这些分支的 PR也可手动运行 |
| **Deploy to Staging** | 推送到 `main``master` 且改动了应用相关路径;也可手动运行 |
| **Deploy to Production** | 仅手动运行,且输入框必须填 **deploy** |
## 针对仓库 `https://gitlink.org.cn/danshen/CI.git` 的操作示例
你的 GitLink 项目页:[danshen/CI](https://gitlink.org.cn/danshen/CI)(默认分支一般是 **`master`**)。流水线入口在顶部 **「流水线(devops)」**。
### 1. 在本机绑定远程并推送PowerShell
在项目根目录执行(若已添加过 `origin`,先 `git remote remove origin`
```powershell
cd d:\Litter\111
git remote add origin https://gitlink.org.cn/danshen/CI.git
git checkout -B master
git add -A
git status
git commit -m "chore: add CI/CD workflows and demo app"
```
若远程已有一次提交(例如只有 `README.md`),第一次推送前需要合并历史:
```powershell
git pull origin master --allow-unrelated-histories
# 若有冲突按提示解决后git add -A && git commit -m "merge remote"
git push -u origin master
```
若你确认可以覆盖远程、只保留本地这一套代码:
```powershell
git push -u origin master --force
```
### 2. 在网页上点哪里
1. 浏览器打开:<https://gitlink.org.cn/danshen/CI>
2. 点 **「流水线(devops)」**,应能看到 **CI Pipeline**、**Deploy to Staging**、**Deploy to Production**。
3. 若列表为空:到 **「代码库」** 确认已推送 **`.github/workflows/`** 或 **`.GitLink/workflows/`** 下的 ymlGitLink 帮助中心里会说明认哪个目录(常见为与 GitHub 类似的 `workflows` 目录)。
### 3. 分支说明
本仓库流水线已对 **`master`**、`main`、`develop` 配置触发;与你当前 GitLink 默认 **`master`** 一致,推送到 `master` 即可跑 CI合并到 `master` 也会触发预发布镜像流水线(在改动命中 `paths` 时)。
---
## 你在 GitLink 上还需要做的事(网页权限)
1. **把本仓库推送到 GitLink**(见上文命令)。
2. 打开 **「流水线(devops)」**,确认能看到上述三个工作流。
3. **若同一次推送触发了两套完全相同的流水线**(因为同时存在 `.github``.GitLink` 两套 yml删掉你平台**不读取**的那一套目录里的 yml 即可,只保留一个目录。
4. 在 **Settings → Environments**(或 GitLink 等价菜单)里新建 **`staging`** 和 **`production`**;生产环境建议开启 **审批人**
5. (可选)在 Secrets 里添加 **`CODECOV_TOKEN`**、在 production 环境配置 **`PROD_API_KEY`**。
6. 若推镜像到 `ghcr.io` 失败:在平台文档中开启 **Packages 写入权限**或按说明改用个人访问令牌PAT登录镜像仓库。
## 开发命令
```bash
npm ci
npm run lint
npm run type-check
npm test
npm run build
```
## 许可证
MIT License

4578
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

21
package.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "gitlink-ci-cd-demo",
"version": "1.0.0",
"description": "GitLink CI/CD Pipeline Demo Project",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"lint": "eslint src/",
"type-check": "tsc --noEmit",
"test": "jest",
"build": "node -e \"require('fs').cpSync('src','dist',{recursive:true})\""
},
"keywords": ["ci-cd", "gitlink", "demo"],
"author": "",
"license": "MIT",
"devDependencies": {
"eslint": "^8.57.0",
"jest": "^29.7.0",
"typescript": "^5.4.5"
}
}

21
src/index.js Normal file
View File

@ -0,0 +1,21 @@
/** @param {string} [name] */
function greet(name = 'World') {
return `Hello, ${name}!`;
}
/** @param {number} a @param {number} b */
function add(a, b) {
return a + b;
}
/** @param {string} email */
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
module.exports = {
greet,
add,
isValidEmail
};

17
test-setup.js Normal file
View File

@ -0,0 +1,17 @@
console.log('=== Node.js 环境测试 ===');
console.log('Node.js 版本:', process.version);
try {
const { greet, add, isValidEmail } = require('./src/index');
console.log('\n=== 功能测试 ===');
console.log('greet():', greet());
console.log('greet("GitLink"):', greet('GitLink'));
console.log('add(2, 3):', add(2, 3));
console.log('isValidEmail("test@example.com"):', isValidEmail('test@example.com'));
console.log('\n✅ 测试通过!项目配置正确!');
} catch (error) {
console.log('\n❌ 测试失败:', error.message);
process.exit(1);
}

43
tests/index.test.js Normal file
View File

@ -0,0 +1,43 @@
const { greet, add, isValidEmail } = require('../src/index');
describe('greet function', () => {
test('greets with default name', () => {
expect(greet()).toBe('Hello, World!');
});
test('greets with custom name', () => {
expect(greet('GitLink')).toBe('Hello, GitLink!');
});
test('handles empty string', () => {
expect(greet('')).toBe('Hello, !');
});
});
describe('add function', () => {
test('adds two positive numbers', () => {
expect(add(2, 3)).toBe(5);
});
test('adds negative numbers', () => {
expect(add(-1, -1)).toBe(-2);
});
test('adds zero', () => {
expect(add(0, 5)).toBe(5);
});
});
describe('isValidEmail function', () => {
test('validates correct email', () => {
expect(isValidEmail('test@example.com')).toBe(true);
});
test('rejects email without @', () => {
expect(isValidEmail('test.example.com')).toBe(false);
});
test('rejects empty email', () => {
expect(isValidEmail('')).toBe(false);
});
});

14
tsconfig.json Normal file
View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"allowJs": true,
"checkJs": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}