Compare commits

63 Commits

Author SHA1 Message Date
jiaojm
c181be2292 修复了了ci的一个格式问题 2025-11-30 18:44:22 +08:00
jiaojm
49009eb9a2 润色脚本格式,添加了install.sh脚本判断网络类型的功能 2025-11-30 16:28:16 +08:00
jiaojm
0ea42c04f5 Merge branch 'main' into tests 2025-11-30 16:12:30 +08:00
孟宁
3567148061 !125 安装脚本支持部署多个容器实例,定制更新了文档相关文件
* 对/docs文档结构做了更新说明
* 对/docs文档结构做了更新说明
* 安装脚本支持部署多个容器实例,定制更新了文档相关文件
2025-11-30 07:29:05 +00:00
jiaojm
8e5edc714f fix 2025-11-30 14:23:39 +08:00
jiaojm
ac1d22708f 修改了相关截图的导出路径 2025-11-30 14:19:51 +08:00
jiaojm
d22a2f30c2 点击安装后经常不跳转,修复这个超时问题 2025-11-30 10:25:37 +08:00
jiaojm
67d8b8b36d fix bug 2025-11-30 10:19:45 +08:00
jiaojm
8de8c86976 fix 2025-11-30 10:02:15 +08:00
jiaojm
1f16d76d96 ci机器里没有ip包,改用docker命令 2025-11-30 09:59:04 +08:00
jiaojm
decbb3336e 修改使install.sh脚本兼容DoD,修复一些错误,增加了一些关键位置的截图和超时判断 2025-11-30 09:52:48 +08:00
vecmatex
adcddd551e !124 修复公开的仓库在用户未登录状态的500错误
* 修复公开的仓库在用户未登录状态的500错误
2025-11-27 03:11:12 +00:00
panshuxiao
63fb8120c9 !123 删除多余的错误测试
Merge pull request !123 from panshuxiao/feature/appstore
2025-11-26 06:41:12 +00:00
jiaojm
dd60ca2100 修改ci脚本和pr评论功能 2025-11-25 19:53:32 +08:00
jiaojm
2795a67741 修改挂载方式 2025-11-25 17:47:49 +08:00
panshuxiao
641a76b773 !119 完成应用商店应用对证书的支持
Merge pull request !119 from panshuxiao/feature/appstore
2025-11-25 09:10:25 +00:00
孟宁
c3b8d4612b 修改了E2E用法文档及CI脚本 2025-11-25 10:35:21 +08:00
jiaojm
6ffbe8d5e4 fix a small bug 2025-11-21 14:22:53 +08:00
jiaojm
a57ed29d28 删除dockerfile,把数据挂载放在脚本里使用 2025-11-21 14:20:24 +08:00
jiaojm
50d912e652 删除了docker-compose文件,现在在run-e2e-tests.sh中构建测试容器,ci脚本也作相应修改 2025-11-21 11:22:30 +08:00
jiaojm
bab3befa45 readme的对应修改 2025-11-20 18:29:26 +08:00
jiaojm
166113b07d 解决冲突,删除不必要的代码 2025-11-20 18:25:32 +08:00
jiaojm
74b0fb4912 解决冲突,删除不必要的代码 2025-11-20 18:23:39 +08:00
jiaojm
ed573cc83b 添加了ci脚本,修改了测试脚本的默认语言为中文 2025-11-20 17:43:35 +08:00
jiaojm
96005ac630 fix a bug 2025-11-20 16:25:23 +08:00
jiaojm
e1137bd7c3 fix 2025-11-20 16:23:27 +08:00
jiaojm
04b17516e4 fix 2025-11-20 16:19:36 +08:00
jiaojm
0acecaad5f 修改了语言的问题,现在统一为中文,不会出现gitea用户为英文的情况 2025-11-20 16:16:42 +08:00
jiaojm
80276e54f9 hot fix 2025-11-20 15:22:41 +08:00
jiaojm
72dfb0aeaf 修改登陆脚本,修改docker网络模式,如果ci成功准备删构建部分 2025-11-20 15:18:39 +08:00
jiaojm
15a471f247 统一了测试脚本为中文,避免了不必要的麻烦,测试目前已经安装成功的ci的稳定性 2025-11-20 15:06:59 +08:00
jiaojm
d1187fd712 fix a bug 2025-11-19 20:48:38 +08:00
jiaojm
b3e31dcefd fix a bug 2025-11-19 20:38:47 +08:00
jiaojm
65e7506979 修改在ci中数据挂载的方式,避免写入root/devstar-data失败导致容器启动出问题 2025-11-19 20:32:06 +08:00
jiaojm
b80c358fda 增加了可选的容器网络模式,修改ci脚本尝试修复devstar启动失败的问题 2025-11-19 20:13:48 +08:00
jiaojm
3d678a982b 增加了语言更换的脚本代码,防止因gitea的语言配置导致测试失败,修改了ci流程 2025-11-19 19:40:18 +08:00
jiaojm
c03be52914 运行ci脚本的相关修改 2025-11-19 17:05:27 +08:00
孟宁
cfffaa1960 !111 基于PlayWright端到端测试,make e2e-test测试devcontainer
1. PlayWright测试脚本
2. 执行make e2e-test测试
3. 把与e2e测试相关的代码都放在了/test/e2e下
2025-11-16 01:29:35 +00:00
jiaojm
dfcf75d7d1 修改了readme,id的问题解决,E2E_NAME的命令格式,一些潜在的sudo问题 2025-11-15 22:12:47 +08:00
jiaojm
7d06daf606 last 2025-11-14 16:30:29 +08:00
jiaojm
5e2987d135 1 2025-11-14 10:07:47 +08:00
jiaojm
8d1f62028c new readme 2025-11-14 09:53:34 +08:00
jiaojm
446e343eab fix 2025-11-14 09:25:43 +08:00
jiaojm
109fdd8136 fix 2025-11-13 21:13:14 +08:00
jiaojm
b3fe3a78e0 1 2025-11-13 15:28:27 +08:00
jiaojm
c6ad67556b 1 2025-11-13 14:13:38 +08:00
jiaojm
5918d57139 1 2025-11-12 20:54:15 +08:00
jiaojm
6b4feabe81 1 2025-11-12 19:52:09 +08:00
jiaojm
fb1637a0f4 1 2025-11-12 18:18:45 +08:00
jiaojm
1fc326dbae 1 2025-11-12 17:21:18 +08:00
jiaojm
25ebc112d1 1 2025-11-12 16:05:30 +08:00
jiaojm
5cba473dd8 new 2025-11-12 13:14:18 +08:00
jiaojm
087519a372 1 2025-11-03 15:14:27 +08:00
jiaojm
a556d823e2 1 2025-10-31 16:27:23 +08:00
jiaojm
b0ec1135c0 1 2025-10-31 16:26:50 +08:00
jiaojm
257941c5e5 new test script 2025-10-31 16:18:47 +08:00
jiaojm
16c817fa9b new 2025-10-24 16:42:10 +08:00
jiaojm
d3f24edcbc new 2025-10-24 16:33:20 +08:00
jiaojm
a99c05bd6e 1 2025-10-24 12:52:58 +08:00
jiaojm
38908ac0aa 测试脚本 2025-10-24 12:50:48 +08:00
jiaojm
b33e4adbe1 test script 2025-10-24 12:03:16 +08:00
jiaojm
cc7f5ccff9 test scrpit 2025-10-24 12:01:35 +08:00
jiaojm
4d4faf5103 test scrpit 2025-10-24 11:59:33 +08:00
repo.diff.stats_desc%!(EXTRA int=43, int=683, int=946)

repo.diff.view_file

@@ -60,6 +60,9 @@ cpu.out
/tests/e2e/reports
/tests/e2e/test-artifacts
/tests/e2e/test-snapshots
/tests/e2e/test-data
/tests/e2e/package-lock.json
/tests/e2e/node_modules/
/tests/*.ini
/node_modules
/yarn.lock

repo.diff.view_file

@@ -0,0 +1,82 @@
name: DevStar E2E Test
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
jobs:
e2e-test:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: build Devstar Image
run: |
make devstar
- name: Install Network Tools
run: |
echo "正在安装 ip 命令..."
sudo apt-get update && sudo apt-get install -y iproute2
- name: start DevStar Container
run: |
# 启动容器,这里的话需要预先的创建宿主机的对应文件夹
LOGS=$(public/assets/install.sh start \
--port=8082 \
--ssh-port=2224 \
--data-dir=/tmp/devstar_ci \
--image=devstar-studio:latest 2>&1)
echo "$LOGS"
TARGET_URL=$(echo "$LOGS" | grep -o 'http://[^ ]*' | tail -1 | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g")
echo "TARGET_URL=$TARGET_URL" >> $GITHUB_ENV
- name: Run E2E Tests
run: |
make e2e-test TARGET_URL="$TARGET_URL"
env:
GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT: "true"
- name: Upload E2E Test Report
if: always()
uses: actions/upload-artifact@v3
with:
name: e2e-test-report
path: tests/e2e/reports/
- name: Post Test Results
if: always() && github.event_name == 'pull_request'
uses: actions/github-script@v6
env:
# 传入任务状态success 或 failure
TEST_RESULT: ${{ job.status }}
with:
script: |
const testResult = process.env.TEST_RESULT || '未知';
const isSuccess = testResult === 'success';
// 动态生成构建链接
const runUrl = `${context.payload.repository.html_url}/actions/runs/${context.runId}`;
const comment = `
## 📋 E2E 测试结果报告
### 测试状态
${isSuccess ? '✅ **通过 (Passed)**' : '❌ **失败 (Failed)**'}
### 🔍 详细报告
HTML 报告已上传至 Artifacts请点击下方链接查看。
👉 [查看运行日志与下载报告](${runUrl})
### 🏗️ 构建信息
- **工作流**: ${context.workflow}
- **提交**: ${context.payload.pull_request.head.sha.slice(0, 7)}
- **触发者**: @${context.actor}
---
> *此评论由 DevStar Actions 自动生成,用于 PR 质量检查。*
`;
// 发送评论
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});

3
.gitignore repo.diff.vendored
repo.diff.view_file

@@ -69,6 +69,9 @@ cpu.out
/tests/e2e/gitea-e2e-*
/tests/e2e/indexers-*
/tests/e2e/reports
/tests/e2e/node_modules/
/tests/e2e/package-lock.json
/tests/e2e/test-data
/tests/e2e/test-artifacts
/tests/e2e/test-snapshots
/tests/*.ini

repo.diff.view_file

@@ -963,11 +963,16 @@ devstar:
echo "Successfully build devstar.cn/devstar/devstar-runtime-container:latest"; \
fi
docker build -t devstar-studio:latest -f docker/Dockerfile.devstar .
.PHONY: docker
docker:
docker build --disable-content-trust=false -t $(DOCKER_REF) .
# support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify" .
# support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify"
.PHONY: e2e-test
e2e-test:
@echo "正在启动E2E-TEST..."
@TARGET_URL=$(TARGET_URL) E2E_USERNAME=$(E2E_USERNAME) E2E_PASSWORD=$(E2E_PASSWORD) ./tests/e2e/run-e2e-tests.sh
# This endif closes the if at the top of the file
endif

208
README.md
repo.diff.view_file

@@ -1,47 +1,29 @@
# DevStar
[![](https://github.com/mengning/DevStar/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/mengning/DevStar/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
[![](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
[![](https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg)](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/mengning/DevStar/releases/latest "GitHub release")
[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=green)](https://gitpod.io/#https://github.com/mengning/DevStar)
[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin")
[繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
The Last Mile of Al for R&D
## Purpose
The goal of this project is to make the easiest, fastest, and most
painless way of setting up a self-hosted Git service.
DevStar Studio是为AI时代打造的新一代一站式智能研发平台面向人类开发者和AI开发者的融合团队专注于产品质量和团队效能为研发部门赋能提效。
As Gitea is written in Go, it works across **all** the platforms and
As [Gitea](https://github.com/go-gitea/gitea) is written in Go, it works across **all** the platforms and
architectures that are supported by Go, including Linux, macOS, and
Windows on x86, amd64, ARM and PowerPC architectures.
This project has been
[forked](https://blog.gitea.com/welcome-to-gitea/) from
[Gogs](https://gogs.io) since November of 2016, but a lot has changed.
For online demonstrations, you can visit [DevStar.cn](https://DevStar.cn).
For accessing free Gitea service (with a limited number of repositories), you can visit [gitea.com](https://gitea.com/user/login).
To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com).
## Documentation
You can find comprehensive documentation on our official [documentation website](https://docs.gitea.com/).
It includes installation, administration, usage, development, contributing guides, and more to help you get started and explore all features effectively.
If you have any suggestions or would like to contribute to it, you can visit the [documentation repository](https://gitea.com/gitea/docs)
* For online demonstrations, you can visit [DevStar.cn](https://DevStar.cn).
* You can find comprehensive documentation on our official [documentation website](https://mengning.com.cn/src/devstar/).
* If you have any suggestions or would like to contribute to it, you can visit [devstar/docs](/docs) and the [documentation repository](https://devstar.cn/devstar/docs)
## Building
From the root of the source tree, run:
Build devstar-studio:latest From the root of the source tree, run::
```
make devstar
```
or Build a binary file named `gitea` From the root of the source tree, run:
TAGS="bindata" make build
@@ -60,18 +42,10 @@ More info: https://docs.gitea.com/installation/install-from-source
## Using
After building, a binary file named `gitea` will be generated in the root of the source tree by default. To run it, use:
./gitea web
> [!NOTE]
> devcontainer相关功能不能在localhost域名下正常工作调试环境请在custom/conf/app.ini中修改为IP地址
> If you're interested in using our APIs, we have experimental support with [documentation](https://docs.gitea.com/api).
Start from Container Image:
Build and Start from Container Image:
```
make devstar
make devstar # Build devstar-studio:latest
public/assets/install.sh start --image=devstar-studio:latest
# 查看日志
@@ -82,6 +56,13 @@ public/assets/install.sh clean
sudo docker stop $(docker ps -aq) && sudo docker rm -f $(docker ps -aq)
```
or, After building, a binary file named `gitea` will be generated in the root of the source tree by default. To run it, use:
./gitea web
> [!NOTE]
> devcontainer相关功能不能在localhost域名下正常工作调试环境请在custom/conf/app.ini中修改为IP地址
## Contributing
Expected workflow is: Fork -> Patch -> Push -> Pull Request
@@ -112,7 +93,7 @@ npm -v # should print `10.9.0`
在DevStar Git仓库创建分支
```
git clone https://gitee.com/devstar/devstar.git
git clone https://devstar.cn/devstar/devstar.git
git checkout -b YOUR_BRANCH
code devstar
@@ -122,6 +103,10 @@ make test # testing
TAGS="bindata timetzdata sqlite sqlite_unlock_notify" make build # 生成可执行文件
./gitea
# 镜像方式安装验证
make devstar # Build devstar-studio:latest
public/assets/install.sh start --image=devstar-studio:latest
# 提交代码
git add FILES
git commit -m "commit log"
@@ -137,140 +122,29 @@ public/assets/install.sh start
> [!NOTE]
>
> 1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
> 2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
## Translating
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com)
Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language, ask one of the managers in the Crowdin project to add a new language there.
You can also just create an issue for adding a language or ask on Discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty, but we hope to fill it as questions pop up.
Get more information from [documentation](https://docs.gitea.com/contributing/localization).
> 2. If you have found a vulnerability in the project, please write privately to **contact@mengning.com.cn**. Thanks!
## Official and Third-Party Projects
We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [tea](https://gitea.com/gitea/tea) and an [action runner](https://gitea.com/gitea/act_runner) for Gitea Action.
* [webTerminal](https://devstar.cn/devstar/webTerminal) for DevStar devcontainer
* [devstar-vscode](https://devstar.cn/devstar/devstar-vscode)
* [action runner](https://devstar.cn/devstar/act_runner) for DevStar Action
* [go-sdk](https://gitea.com/gitea/go-sdk)
* a CLI tool called [tea](https://gitea.com/gitea/tea)
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea), where you can discover more third-party projects, including SDKs, plugins, themes, and more.
## Communication
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
If you have questions that are not covered by the [documentation](https://docs.gitea.com/), you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
## Authors
- [Maintainers](https://github.com/orgs/go-gitea/people)
- [Contributors](https://github.com/mengning/DevStar/graphs/contributors)
- [Translators](options/locale/TRANSLATORS)
## Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/gitea#backer)]
<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
## Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/gitea#sponsor)]
<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
## FAQ
**How do you pronounce Gitea?**
Gitea is pronounced [/ɡɪti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea" with a hard g.
**Why is this not hosted on a Gitea instance?**
We're [working on it](https://github.com/mengning/DevStar/issues/1029).
**Where can I find the security patches?**
In the [release log](https://github.com/mengning/DevStar/releases) or the [change log](https://github.com/mengning/DevStar/blob/main/CHANGELOG.md), search for the keyword `SECURITY` to find the security patches.
## License
This project is licensed under the MIT License.
See the [LICENSE](https://github.com/mengning/DevStar/blob/main/LICENSE) file
for the full license text.
This project is licensed under the AGPL-3.0 License.
## Further information
> * **Community Edition (Free):**
> * Includes the standalone version of DevStar Studio.
> * Free to use and deploy.
> * Technical support is not included.
> * **Enterprise Edition (Paid):**
> * Requires a commercial license.
> * Unlocks Kubernetes-related features and other advanced functionalities.
> * Includes technical support.
<details>
<summary>Looking for an overview of the interface? Check it out!</summary>
### Login/Register Page
![Login](https://dl.gitea.com/screenshots/login.png)
![Register](https://dl.gitea.com/screenshots/register.png)
### User Dashboard
![Home](https://dl.gitea.com/screenshots/home.png)
![Issues](https://dl.gitea.com/screenshots/issues.png)
![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png)
![Milestones](https://dl.gitea.com/screenshots/milestones.png)
### User Profile
![Profile](https://dl.gitea.com/screenshots/user_profile.png)
### Explore
![Repos](https://dl.gitea.com/screenshots/explore_repos.png)
![Users](https://dl.gitea.com/screenshots/explore_users.png)
![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png)
### Repository
![Home](https://dl.gitea.com/screenshots/repo_home.png)
![Commits](https://dl.gitea.com/screenshots/repo_commits.png)
![Branches](https://dl.gitea.com/screenshots/repo_branches.png)
![Labels](https://dl.gitea.com/screenshots/repo_labels.png)
![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png)
![Releases](https://dl.gitea.com/screenshots/repo_releases.png)
![Tags](https://dl.gitea.com/screenshots/repo_tags.png)
#### Repository Issue
![List](https://dl.gitea.com/screenshots/repo_issues.png)
![Issue](https://dl.gitea.com/screenshots/repo_issue.png)
#### Repository Pull Requests
![List](https://dl.gitea.com/screenshots/repo_pull_requests.png)
![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png)
![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png)
![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png)
#### Repository Actions
![List](https://dl.gitea.com/screenshots/repo_actions.png)
![Details](https://dl.gitea.com/screenshots/repo_actions_run.png)
#### Repository Activity
![Activity](https://dl.gitea.com/screenshots/repo_activity.png)
![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png)
![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png)
![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png)
### Organization
![Home](https://dl.gitea.com/screenshots/org_home.png)
</details>
See the [LICENSE](https://github.com/mengning/DevStar/blob/main/LICENSE) file for the full license text.

repo.diff.view_file

@@ -1,206 +0,0 @@
# Gitea
[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
[![](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
[![](https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg)](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=green)](https://gitpod.io/#https://github.com/go-gitea/gitea)
[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin")
[English](./README.md) | [繁體中文](./README.zh-tw.md)
## 目的
这个项目的目标是提供最简单、最快速、最无痛的方式来设置自托管的 Git 服务。
由于 Gitea 是用 Go 语言编写的,它可以在 Go 支持的所有平台和架构上运行,包括 Linux、macOS 和 Windows 的 x86、amd64、ARM 和 PowerPC 架构。这个项目自 2016 年 11 月从 [Gogs](https://gogs.io) [分叉](https://blog.gitea.com/welcome-to-gitea/) 而来,但已经有了很多变化。
在线演示可以访问 [demo.gitea.com](https://demo.gitea.com)。
要访问免费的 Gitea 服务(有一定数量的仓库限制),可以访问 [gitea.com](https://gitea.com/user/login)。
要快速部署您自己的专用 Gitea 实例,可以在 [cloud.gitea.com](https://cloud.gitea.com) 开始免费试用。
## 文件
您可以在我们的官方 [文件网站](https://docs.gitea.com/) 上找到全面的文件。
它包括安装、管理、使用、开发、贡献指南等,帮助您快速入门并有效地探索所有功能。
如果您有任何建议或想要贡献,可以访问 [文件仓库](https://gitea.com/gitea/docs)
## 构建
从源代码树的根目录运行:
TAGS="bindata" make build
如果需要 SQLite 支持:
TAGS="bindata sqlite sqlite_unlock_notify" make build
`build` 目标分为两个子目标:
- `make backend` 需要 [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定义。
- `make frontend` 需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
需要互联网连接来下载 go 和 npm 模块。从包含预构建前端文件的官方源代码压缩包构建时,不会触发 `frontend` 目标,因此可以在没有 Node.js 的情况下构建。
更多信息https://docs.gitea.com/installation/install-from-source
## 使用
构建后,默认情况下会在源代码树的根目录生成一个名为 `gitea` 的二进制文件。要运行它,请使用:
./gitea web
> [!注意]
> 如果您对使用我们的 API 感兴趣,我们提供了实验性支持,并附有 [文件](https://docs.gitea.com/api)。
## 贡献
预期的工作流程是Fork -> Patch -> Push -> Pull Request
> [!注意]
>
> 1. **在开始进行 Pull Request 之前,您必须阅读 [贡献者指南](CONTRIBUTING.md)。**
> 2. 如果您在项目中发现了漏洞,请私下写信给 **security@gitea.io**。谢谢!
## 翻译
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com)
翻译通过 [Crowdin](https://translate.gitea.com) 进行。如果您想翻译成新的语言,请在 Crowdin 项目中请求管理员添加新语言。
您也可以创建一个 issue 来添加语言,或者在 discord 的 #translation 频道上询问。如果您需要上下文或发现一些翻译问题,可以在字符串上留言或在 Discord 上询问。对于一般的翻译问题,文档中有一个部分。目前有点空,但我们希望随着问题的出现而填充它。
更多信息请参阅 [文件](https://docs.gitea.com/contributing/localization)。
## 官方和第三方项目
我们提供了一个官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一个名为 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一个 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
我们在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 维护了一个 Gitea 相关项目的列表,您可以在那里发现更多的第三方项目,包括 SDK、插件、主题等。
## 通讯
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
如果您有任何文件未涵盖的问题,可以在我们的 [Discord 服务器](https://discord.gg/Gitea) 上与我们联系,或者在 [discourse 论坛](https://forum.gitea.com/) 上创建帖子。
## 作者
- [维护者](https://github.com/orgs/go-gitea/people)
- [贡献者](https://github.com/go-gitea/gitea/graphs/contributors)
- [翻译者](options/locale/TRANSLATORS)
## 支持者
感谢所有支持者! 🙏 [[成为支持者](https://opencollective.com/gitea#backer)]
<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
## 赞助商
通过成为赞助商来支持这个项目。您的标志将显示在这里,并带有链接到您的网站。 [[成为赞助商](https://opencollective.com/gitea#sponsor)]
<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
## 常见问题
**Gitea 怎么发音?**
Gitea 的发音是 [/ɡɪti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea" 一样g 是硬音。
**为什么这个项目没有托管在 Gitea 实例上?**
我们正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。
**在哪里可以找到安全补丁?**
在 [发布日志](https://github.com/go-gitea/gitea/releases) 或 [变更日志](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索关键词 `SECURITY` 以找到安全补丁。
## 许可证
这个项目是根据 MIT 许可证授权的。
请参阅 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以获取完整的许可证文本。
## 进一步信息
<details>
<summary>寻找界面概述?查看这里!</summary>
### 登录/注册页面
![Login](https://dl.gitea.com/screenshots/login.png)
![Register](https://dl.gitea.com/screenshots/register.png)
### 用户仪表板
![Home](https://dl.gitea.com/screenshots/home.png)
![Issues](https://dl.gitea.com/screenshots/issues.png)
![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png)
![Milestones](https://dl.gitea.com/screenshots/milestones.png)
### 用户资料
![Profile](https://dl.gitea.com/screenshots/user_profile.png)
### 探索
![Repos](https://dl.gitea.com/screenshots/explore_repos.png)
![Users](https://dl.gitea.com/screenshots/explore_users.png)
![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png)
### 仓库
![Home](https://dl.gitea.com/screenshots/repo_home.png)
![Commits](https://dl.gitea.com/screenshots/repo_commits.png)
![Branches](https://dl.gitea.com/screenshots/repo_branches.png)
![Labels](https://dl.gitea.com/screenshots/repo_labels.png)
![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png)
![Releases](https://dl.gitea.com/screenshots/repo_releases.png)
![Tags](https://dl.gitea.com/screenshots/repo_tags.png)
#### 仓库问题
![List](https://dl.gitea.com/screenshots/repo_issues.png)
![Issue](https://dl.gitea.com/screenshots/repo_issue.png)
#### 仓库拉取请求
![List](https://dl.gitea.com/screenshots/repo_pull_requests.png)
![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png)
![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png)
![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png)
#### 仓库操作
![List](https://dl.gitea.com/screenshots/repo_actions.png)
![Details](https://dl.gitea.com/screenshots/repo_actions_run.png)
#### 仓库活动
![Activity](https://dl.gitea.com/screenshots/repo_activity.png)
![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png)
![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png)
![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png)
### 组织
![Home](https://dl.gitea.com/screenshots/org_home.png)
</details>

repo.diff.view_file

@@ -1,206 +0,0 @@
# Gitea
[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
[![](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
[![](https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg)](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=green)](https://gitpod.io/#https://github.com/go-gitea/gitea)
[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin")
[English](./README.md) | [简体中文](./README.zh-cn.md)
## 目的
這個項目的目標是提供最簡單、最快速、最無痛的方式來設置自託管的 Git 服務。
由於 Gitea 是用 Go 語言編寫的,它可以在 Go 支援的所有平台和架構上運行,包括 Linux、macOS 和 Windows 的 x86、amd64、ARM 和 PowerPC 架構。這個項目自 2016 年 11 月從 [Gogs](https://gogs.io) [分叉](https://blog.gitea.com/welcome-to-gitea/) 而來,但已經有了很多變化。
在線演示可以訪問 [demo.gitea.com](https://demo.gitea.com)。
要訪問免費的 Gitea 服務(有一定數量的倉庫限制),可以訪問 [gitea.com](https://gitea.com/user/login)。
要快速部署您自己的專用 Gitea 實例,可以在 [cloud.gitea.com](https://cloud.gitea.com) 開始免費試用。
## 文件
您可以在我們的官方 [文件網站](https://docs.gitea.com/) 上找到全面的文件。
它包括安裝、管理、使用、開發、貢獻指南等,幫助您快速入門並有效地探索所有功能。
如果您有任何建議或想要貢獻,可以訪問 [文件倉庫](https://gitea.com/gitea/docs)
## 構建
從源代碼樹的根目錄運行:
TAGS="bindata" make build
如果需要 SQLite 支援:
TAGS="bindata sqlite sqlite_unlock_notify" make build
`build` 目標分為兩個子目標:
- `make backend` 需要 [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定義。
- `make frontend` 需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
需要互聯網連接來下載 go 和 npm 模塊。從包含預構建前端文件的官方源代碼壓縮包構建時,不會觸發 `frontend` 目標,因此可以在沒有 Node.js 的情況下構建。
更多信息https://docs.gitea.com/installation/install-from-source
## 使用
構建後,默認情況下會在源代碼樹的根目錄生成一個名為 `gitea` 的二進制文件。要運行它,請使用:
./gitea web
> [!注意]
> 如果您對使用我們的 API 感興趣,我們提供了實驗性支援,並附有 [文件](https://docs.gitea.com/api)。
## 貢獻
預期的工作流程是Fork -> Patch -> Push -> Pull Request
> [!注意]
>
> 1. **在開始進行 Pull Request 之前,您必須閱讀 [貢獻者指南](CONTRIBUTING.md)。**
> 2. 如果您在項目中發現了漏洞,請私下寫信給 **security@gitea.io**。謝謝!
## 翻譯
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com)
翻譯通過 [Crowdin](https://translate.gitea.com) 進行。如果您想翻譯成新的語言,請在 Crowdin 項目中請求管理員添加新語言。
您也可以創建一個 issue 來添加語言,或者在 discord 的 #translation 頻道上詢問。如果您需要上下文或發現一些翻譯問題,可以在字符串上留言或在 Discord 上詢問。對於一般的翻譯問題,文檔中有一個部分。目前有點空,但我們希望隨著問題的出現而填充它。
更多信息請參閱 [文件](https://docs.gitea.com/contributing/localization)。
## 官方和第三方項目
我們提供了一個官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一個名為 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一個 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
我們在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 維護了一個 Gitea 相關項目的列表,您可以在那裡發現更多的第三方項目,包括 SDK、插件、主題等。
## 通訊
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
如果您有任何文件未涵蓋的問題,可以在我們的 [Discord 服務器](https://discord.gg/Gitea) 上與我們聯繫,或者在 [discourse 論壇](https://forum.gitea.com/) 上創建帖子。
## 作者
- [維護者](https://github.com/orgs/go-gitea/people)
- [貢獻者](https://github.com/go-gitea/gitea/graphs/contributors)
- [翻譯者](options/locale/TRANSLATORS)
## 支持者
感謝所有支持者! 🙏 [[成為支持者](https://opencollective.com/gitea#backer)]
<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
## 贊助商
通過成為贊助商來支持這個項目。您的標誌將顯示在這裡,並帶有鏈接到您的網站。 [[成為贊助商](https://opencollective.com/gitea#sponsor)]
<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
## 常見問題
**Gitea 怎麼發音?**
Gitea 的發音是 [/ɡɪti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea" 一樣g 是硬音。
**為什麼這個項目沒有託管在 Gitea 實例上?**
我們正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。
**在哪裡可以找到安全補丁?**
在 [發佈日誌](https://github.com/go-gitea/gitea/releases) 或 [變更日誌](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索關鍵詞 `SECURITY` 以找到安全補丁。
## 許可證
這個項目是根據 MIT 許可證授權的。
請參閱 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以獲取完整的許可證文本。
## 進一步信息
<details>
<summary>尋找界面概述?查看這裡!</summary>
### 登錄/註冊頁面
![Login](https://dl.gitea.com/screenshots/login.png)
![Register](https://dl.gitea.com/screenshots/register.png)
### 用戶儀表板
![Home](https://dl.gitea.com/screenshots/home.png)
![Issues](https://dl.gitea.com/screenshots/issues.png)
![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png)
![Milestones](https://dl.gitea.com/screenshots/milestones.png)
### 用戶資料
![Profile](https://dl.gitea.com/screenshots/user_profile.png)
### 探索
![Repos](https://dl.gitea.com/screenshots/explore_repos.png)
![Users](https://dl.gitea.com/screenshots/explore_users.png)
![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png)
### 倉庫
![Home](https://dl.gitea.com/screenshots/repo_home.png)
![Commits](https://dl.gitea.com/screenshots/repo_commits.png)
![Branches](https://dl.gitea.com/screenshots/repo_branches.png)
![Labels](https://dl.gitea.com/screenshots/repo_labels.png)
![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png)
![Releases](https://dl.gitea.com/screenshots/repo_releases.png)
![Tags](https://dl.gitea.com/screenshots/repo_tags.png)
#### 倉庫問題
![List](https://dl.gitea.com/screenshots/repo_issues.png)
![Issue](https://dl.gitea.com/screenshots/repo_issue.png)
#### 倉庫拉取請求
![List](https://dl.gitea.com/screenshots/repo_pull_requests.png)
![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png)
![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png)
![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png)
#### 倉庫操作
![List](https://dl.gitea.com/screenshots/repo_actions.png)
![Details](https://dl.gitea.com/screenshots/repo_actions_run.png)
#### 倉庫活動
![Activity](https://dl.gitea.com/screenshots/repo_activity.png)
![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png)
![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png)
![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png)
### 組織
![Home](https://dl.gitea.com/screenshots/org_home.png)
</details>

15
docs/README.md Normal file
repo.diff.view_file

@@ -0,0 +1,15 @@
# DevStar Design文档相关列表
设计相关的放在devstar/devstar仓库/docs目录下用户使用相关文档放在devstar/docs仓库并将两者对应起来如下
## CI/CD Workflow ( Actions/[act_runner](https://devstar.cn/devstar/act_runner) )
* [安装配置和使用](https://devstar.cn/devstar/docs/)
* 设计说明
* actions ...
## 开发环境容器Devcontainer
* 安装配置使用说明
* [webTerminal](https://devstar.cn/devstar/webTerminal) for DevStar devcontainer
* [devstar-vscode](https://devstar.cn/devstar/devstar-vscode)

repo.diff.view_file

@@ -1,124 +0,0 @@
# Devstar 部署文档
## 1. 安装 Helm
在开始部署前,请先安装 Helm。建议使用官方提供的安装脚本具体步骤可参考安装 Helm | Helm。
推荐使用 `get_helm.sh`脚本进行安装,执行如下命令:
```
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
```
## 2. 获取并准备部署文件
在待部署的机器上新建一个目录,然后将 DevStar 的 Helm Chart 仓库克隆到该目录中。请注意,该仓库为私有仓库,需先获取访问权限。
如部署目标为 `devstar.cn`,请确保切换到对应的分支(如图所示):
![image-20251104221729545](./assets/image-20251104221729545.png)
将代码仓库克隆到本地后,目录中应包含以下四个脚本文件:
![image-20251104221749140](./assets/image-20251104221749140.png)
## 3. 首次安装
执行 `step1-install-helm.sh`脚本进行首次安装。安装时间取决于网络状况和镜像拉取速度,请耐心等待。
安装完成后,使用以下命令检查 Pod 状态:
```
kubectl get pods -n devstar-studio-ns
```
如发现 Pod 状态异常,可使用如下命令排查:
- 查看 Pod 日志:
```
kubectl logs -n devstar-studio-ns <pod名称>
```
- 查看 Pod 详细信息:
```
kubectl describe pod -n devstar-studio-ns <pod名称>
```
首次安装时Pod 可能处于 `Pending`状态,通常是由于 PVCPersistentVolumeClaim未绑定到对应的 PVPersistentVolume所致。请检查 PV 与 PVC 的状态:
```
kubectl get pv -A
kubectl get pvc -A
```
![image-20251104221811555](./assets/image-20251104221811555.png)
如发现有 PVC 处于 `Pending`状态,请手动创建并绑定对应的 PV。以下为 PV 的示例 YAML 配置,请根据实际情况修改相应字段:
```
apiVersion: v1
kind: PersistentVolume
metadata:
name: node2-local-pv-gitea # 请根据实际情况修改名称
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
storageClassName: local
persistentVolumeReclaimPolicy: Retain
local:
path: /mnt/datadisk/devstar/gitea-storage # 修改为实际存储路径
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node2 # 修改为实际节点名称
volumeMode: Filesystem
claimRef:
name: gitea-shared-storage-claim
namespace: devstar-studio-ns
```
使用以下命令应用 PV 配置:
```
kubectl apply -f <yaml文件名>
```
## 4. 域名解析与证书配置
如部署环境为公网可访问(如 `devstar.cn`),请在腾讯云(或其他域名服务商)控制台中配置域名解析,并申请及配置 HTTPS 证书。具体操作请参考相关证书配置文档。
## 5. 更新部署
若修改了 `values.yaml`文件,请执行 `step2-upgrade-helm.sh`脚本进行更新。更新完成后,会看到类似如下提示:
![image-20251104221928154](./assets/image-20251104221928154.png)
更新后请再次检查 Pod 状态:
```
kubectl get pods -n devstar-studio-ns
```
## 6. 验证部署版本
您可以通过以下两种方式确认 DevStar 的版本是否更新成功:
1. **在 DevStar 主界面查看**登录系统后,可在主界面右下角查看当前版本号。
![image-20251104221953043](./assets/image-20251104221953043.png)
2. **在流水线页面查看**进入“工作流”→“流水线”,在如图所示位置也可查看版本信息:
![image-20251104222023293](./assets/image-20251104222023293.png)
通过比对版本号,即可确认系统是否已成功更新至目标版本。

repo.diff.view_file

@@ -1,47 +0,0 @@
# SSL 证书配置与续期指南
目前需要配置证书的有devstar.cn和dev.devstar.cn
## 一、证书申请与续期
腾讯云提供的免费 HTTPS 证书有效期为 **90 天**,系统会在到期前通过短信提醒您及时续期。
### 操作步骤:
登录腾讯云控制台,搜索进入 **SSL 证书**管理页面。
![image-20251104224918197](./assets/image-20251104224918197.png)
在证书列表中查看已申请证书的有效期及到期时间。
![image-20251104224951191](./assets/image-20251104224951191.png)
点击右侧的 **快速续期**进入续期界面。
![image-20251104225007427](./assets/image-20251104225007427.png)
![image-20251104225042423](./assets/image-20251104225042423.png)
选择 **自动 DNS 验证**并勾选 **自动删除旧验证记录**,提交后等待证书签发。
证书签发后,在下载界面选择 **Nginx**格式,下载包含 `.crt``.key`文件的证书包。
![image-20251104225305942](./assets/image-20251104225305942.png)
在master节点登录集群进入当前要更新的域名对应的文件夹
![image-20251104225334394](./assets/image-20251104225334394.png)
## 二、证书部署
### 部署流程:
登录 Kubernetes 集群的 Master 节点,进入目标域名对应的证书目录。
将下载的 `.crt``.key`文件上传至服务器,覆盖原有证书文件。
执行相应的脚本完成证书更新:**首次部署**:使用 `make-k8s-tls-secret.sh`**证书续期**:使用 `update-xxx-tls-secret.sh`
![image-20251104225929257](./assets/image-20251104225929257.png)

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 15 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 14 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 56 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 56 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 55 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 51 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 7.9 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 7.9 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 128 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 15 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 41 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 107 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 29 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 7.5 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 7.5 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 21 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 199 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 187 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 69 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 192 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 96 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 49 KiB

repo.diff.bin_not_shown

repo.diff.file_before

repo.diff.file_image_width:  |  repo.diff.file_image_height:  |  repo.diff.file_byte_size: 32 KiB

repo.diff.view_file

@@ -1,98 +1,53 @@
import {devices} from '@playwright/test';
import {env} from 'node:process';
import type {PlaywrightTestConfig} from '@playwright/test';
import { devices } from '@playwright/test';
import { env } from 'node:process';
import type { PlaywrightTestConfig } from '@playwright/test';
const BASE_URL = env.GITEA_URL?.replace?.(/\/$/g, '') || 'http://localhost:3000';
const BASE_URL = env.DEVSTAR_URL?.replace?.(/\/$/g, '') || 'http://localhost:3000';
export default {
testDir: './tests/e2e/',
testMatch: /.*\.test\.e2e\.ts/, // Match any .test.e2e.ts files
/* Maximum time one test can run for. */
timeout: 30 * 1000,
const config: PlaywrightTestConfig = {
testDir: './specs',
testMatch: /specs\/.*\.test\.ts/,
timeout: 500000,
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: 2000,
timeout: 15000,
},
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: Boolean(env.CI),
/* Retry on CI only */
retries: env.CI ? 2 : 0,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: env.CI ? 'list' : [['list'], ['html', {outputFolder: 'tests/e2e/reports/', open: 'never'}]],
reporter: [['html', {
outputFolder: 'playwright-report/html',
open: 'never'
}]],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
headless: true, // set to false to debug
locale: 'en-US',
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 1000,
/* Maximum time allowed for navigation, such as `page.goto()`. */
navigationTimeout: 5 * 1000,
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: BASE_URL,
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
headless: true,
locale: 'zh-CN',
timezoneId: 'Asia/Shanghai',
actionTimeout: 15000,
navigationTimeout: 15000,
baseURL: BASE_URL,
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
/* Project-specific settings. */
use: {
...devices['Desktop Chrome'],
},
},
// disabled because of https://github.com/go-gitea/gitea/issues/21355
// {
// name: 'firefox',
// use: {
// ...devices['Desktop Firefox'],
// },
// },
{
name: 'webkit',
use: {
...devices['Desktop Safari'],
},
},
/* Test against mobile viewports. */
{
name: 'Mobile Chrome',
use: {
...devices['Pixel 5'],
},
},
{
name: 'Mobile Safari',
use: {
...devices['iPhone 12'],
},
},
],
outputDir: 'playwright-report/test-artifacts/',
snapshotDir: 'playwright-report/test-snapshots/',
};
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
outputDir: 'tests/e2e/test-artifacts/',
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
snapshotDir: 'tests/e2e/test-snapshots/',
} satisfies PlaywrightTestConfig;
const skipInstall = env.E2E_SKIP_INSTALL === 'true';
if (skipInstall) {
console.log(`已跳过 global-setup.`);
} else {
console.log(`[Playwright Config] 探测结果: E2E_SKIP_INSTALL is not 'true'. 已启用 global-setup.`);
config.globalSetup = require.resolve('./global-setup.ts');
}
export default config satisfies PlaywrightTestConfig;

repo.diff.view_file

@@ -2,14 +2,13 @@
# Copyright 2024 Mengning Software All rights reserved.
# 默认值
NAME=DevStar-Studio
NAME="DevStar-Studio-$(date +%Y%m%d-%H-%M-%S)"
IMAGE_REGISTRY_USER=mengning997
IMAGE_NAME=devstar-studio
VERSION=latest # DevStar Studio的默认版本为最新版本
PORT=80 # 设置端口默认值为 80
SSH_PORT=2222 # 设置ssh默认端口号2222
DATA_DIR=${HOME}/devstar_data
APP_INI=${DATA_DIR}/app.ini
# 错误处理函数
error_handler() {
@@ -111,25 +110,37 @@ function start {
mkdir -p $DATA_DIR
sudo chown 1000:1000 $DATA_DIR
sudo chmod 666 /var/run/docker.sock
if [ ! -f "$APP_INI" ]; then
DOMAIN_NAME=$(hostname -I | awk '{print $1}')
DOMAIN_NAME=$(ip route get 1 2>/dev/null | awk '{print $7; exit}')
if [[ -f "/.dockerenv" ]]; then
if [[ -S "/var/run/docker.sock" ]] && command -v docker >/dev/null 2>&1; then
if docker info 2>/dev/null | grep -q "Storage Driver: vfs"; then
# DinD 环境 - 保持原来的容器IP
: # 什么也不做,使用初始值
else
# DooD 环境 - 获取宿主机IP
DOMAIN_NAME=$(ip route | grep default | awk '{print $3}' 2>/dev/null)
fi
fi
# 普通容器环境保持原来的容器IP
fi
if [ ! -f "${DATA_DIR}/app.ini" ]; then
echo "DOMAIN_NAME=$DOMAIN_NAME"
else
# 读取 DOMAIN 值
DOMAIN_NAME=$(grep -E '^\s*DOMAIN\s*=' "$APP_INI" | cut -d'=' -f2 | xargs)
DOMAIN_NAME_APP_INI=$(grep -E '^\s*DOMAIN\s*=' "${DATA_DIR}/app.ini" | cut -d'=' -f2 | xargs)
# 检查是否成功读取到值
if [[ -z "$DOMAIN_NAME" ]]; then
DOMAIN_NAME="localhost"
if [[ -n "$DOMAIN_NAME_APP_INI" ]]; then
DOMAIN_NAME=$DOMAIN_NAME_APP_INI
fi
echo "DOMAIN_NAME=$DOMAIN_NAME"
fi
# 启动devstar-studio容器
stop
if [[ -z "$IMAGE_STR" ]]; then
IMAGE_STR="$IMAGE_REGISTRY_USER/$IMAGE_NAME:$VERSION"
fi
echo "image=$IMAGE_STR"
sudo docker run --restart=always --name $NAME -d -p $PORT:3000 -p $SSH_PORT:$SSH_PORT -v /var/run/docker.sock:/var/run/docker.sock -v ~/devstar_data:/var/lib/gitea -v ~/devstar_data:/etc/gitea $IMAGE_STR
sudo docker run --restart=always --name $NAME -d -p $PORT:3000 -p $SSH_PORT:$SSH_PORT -v /var/run/docker.sock:/var/run/docker.sock -v ${DATA_DIR}:/var/lib/gitea -v ${DATA_DIR}:/etc/gitea $IMAGE_STR
# 打开 `http://localhost:8080` 完成安装。
success "-------------------------------------------------------"
success "DevStar started in http://$DOMAIN_NAME:$PORT successfully!"
@@ -139,30 +150,37 @@ function start {
# Function to stop
function stop {
if [ $(docker ps -a --filter "name=^/${NAME}$" -q | wc -l) -gt 0 ]; then
sudo docker stop $NAME && sudo docker rm -f $NAME
fi
if [ $(docker ps -a --filter "name=^/devstar-studio$" -q | wc -l) -gt 0 ]; then
sudo docker stop devstar-studio && sudo docker rm -f devstar-studio
if [ $(docker ps -a --filter "name=^/DevStar-Studio.*|devstar-studio.*" -q | wc -l) -gt 0 ]; then
sudo docker stop $(docker ps -a --filter "name=^/DevStar-Studio.*|devstar-studio.*" -q) && \
sudo docker rm -f $(docker ps -a --filter "name=^/DevStar-Studio.*|devstar-studio.*" -q)
fi
if [ $(docker ps -a --filter "name=^/webterminal-" -q | wc -l) -gt 0 ]; then
sudo docker stop $(docker ps -a --filter "name=^/webterminal-" -q) && sudo docker rm -f $(docker ps -a --filter "name=^/webterminal-" -q)
fi
if [ $(docker ps -a --filter "name=^/runner-" -q | wc -l) -gt 0 ]; then
sudo docker stop $(docker ps -a --filter "name=^/runner-" -q) && sudo docker rm -f $(docker ps -a --filter "name=^/runner-" -q)
fi
fi
}
# Function to logs
function logs {
# 查看devstar-studio容器的运行日志
sudo docker logs $NAME
sudo docker ps -a --filter "name=^/DevStar-Studio.*|devstar-studio.*" --format "table {{.ID}}\t{{.Names}}" | tail -n +2 | while read container_id container_name; do
echo "=== 容器日志: $container_name (ID: $container_id) ==="
sudo docker logs "$container_id"
done
}
# Function to clean
function clean {
stop
rm -rf $DATA_DIR
read -p "警告:即将永久删除目录 $DATA_DIR,此操作不可恢复!请输入 'YES' 确认: " confirm
if [[ "$confirm" == "YES" ]]; then
rm -rf "$DATA_DIR"
echo "目录已删除"
else
echo "操作已取消"
fi
}
# Function to usage help
@@ -173,6 +191,7 @@ function usage {
success " start Start DevStar Studio"
success " --port=<arg> Specify the port number (default port is 80)"
success " --ssh-port=<arg> Specify the ssh-port number (default ssh-port is 2222)"
success " --data-dir=<arg> Specify the data directory (default data directory is ~/devstar_data)"
success " --version=<arg> Specify the DevStar Studio Image Version (default verson is latest)"
success " --image=<arg> Specify the DevStar Studio Image example: devstar-studio:latest "
success " stop Stop the running DevStar Studio"
@@ -188,7 +207,7 @@ case "$1" in
usage
;;
start)
ARGS=$(getopt --long port::,ssh-port::,version::,image:: -- "$@")
ARGS=$(getopt --long port::,ssh-port::,data-dir::,version::,image:: -- "$@")
if [ $? -ne 0 ]; then
failure "ARGS ERROR!"
exit 1
@@ -205,6 +224,10 @@ case "$1" in
SSH_PORT="$2"
echo "The SSH_Port is: $SSH_PORT"
shift 2 ;;
--data-dir)
DATA_DIR="$2"
echo "The SSH_Port is: $DATA_DIR"
shift 2 ;;
--version)
VERSION="$2"
echo "The DevStar Studio Image Version is: $VERSION"
@@ -246,5 +269,4 @@ case "$1" in
devstar help
fi
;;
esac
esac

repo.diff.view_file

@@ -400,25 +400,25 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
if ctx.Doer != nil {
ctx.Data["AllowCreateDevcontainer"] = ctx.Doer.AllowCreateDevcontainer
ctx.Data["AllowCreateActRunner"] = ctx.Doer.AllowCreateActRunner
} else {
query := ctx.Req.URL.Query()
userID := query.Get("user")
userNum, err := strconv.ParseInt(userID, 10, 64)
if err != nil {
return
}
u, err := user_model.GetUserByID(ctx, userNum)
if err != nil {
if user_model.IsErrUserNotExist(err) {
ctx.Redirect(setting.AppSubURL + "/-/admin/users")
} else {
ctx.ServerError("GetUserByID", err)
if userID != "" {
userNum, err := strconv.ParseInt(userID, 10, 64)
if err == nil {
u, err := user_model.GetUserByID(ctx, userNum)
if err != nil {
if user_model.IsErrUserNotExist(err) {
ctx.Redirect(setting.AppSubURL + "/-/admin/users")
} else {
ctx.ServerError("GetUserByID", err)
}
return
}
ctx.Data["AllowCreateDevcontainer"] = u.AllowCreateDevcontainer
ctx.Data["AllowCreateActRunner"] = u.AllowCreateActRunner
}
return
}
ctx.Data["AllowCreateDevcontainer"] = u.AllowCreateDevcontainer
ctx.Data["AllowCreateActRunner"] = u.AllowCreateActRunner
}
if repo.IsMirror {

repo.diff.view_file

@@ -36,7 +36,7 @@
{{template "custom/extra_links" .}}
{{if not .IsSigned}}
<a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com">{{ctx.Locale.Tr "help"}}</a>
<a class="item" target="_blank" rel="noopener noreferrer" href="https://mengning.com.cn/src/devstar/">{{ctx.Locale.Tr "help"}}</a>
{{end}}
</div>
@@ -116,7 +116,7 @@
{{svg "octicon-tools"}}
{{ctx.Locale.Tr "your_settings"}}
</a>
<a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com">
<a class="item" target="_blank" rel="noopener noreferrer" href="https://mengning.com.cn/src/devstar/">
{{svg "octicon-question"}}
{{ctx.Locale.Tr "help"}}
</a>

repo.diff.view_file

@@ -1,92 +1,27 @@
# End to end tests
# E2E端到端测试
E2e tests largely follow the same syntax as [integration tests](../integration).
Whereas integration tests are intended to mock and stress the back-end, server-side code, e2e tests the interface between front-end and back-end, as well as visual regressions with both assertions and visual comparisons.
They can be run with make commands for the appropriate backends, namely:
```shell
make test-sqlite
make test-pgsql
make test-mysql
make test-mssql
```
## E2E端到端测试的用法
Make sure to perform a clean front-end build before running tests:
```
make clean frontend
```
## Install playwright system dependencies
```
npx playwright install-deps
```
## Run all tests via local act_runner
```
act_runner exec -W ./.github/workflows/pull-e2e-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest
```
## Run sqlite e2e tests
Start tests
```
make test-e2e-sqlite
```
## Run MySQL e2e tests
Setup a MySQL database inside docker
```
docker run -e "MYSQL_DATABASE=test" -e "MYSQL_ALLOW_EMPTY_PASSWORD=yes" -p 3306:3306 --rm --name mysql mysql:latest #(just ctrl-c to stop db and clean the container)
docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" --rm --name elasticsearch elasticsearch:7.6.0 #(in a second terminal, just ctrl-c to stop db and clean the container)
```
Start tests based on the database container
```
TEST_MYSQL_HOST=localhost:3306 TEST_MYSQL_DBNAME=test TEST_MYSQL_USERNAME=root TEST_MYSQL_PASSWORD='' make test-e2e-mysql
```
## Run pgsql e2e tests
Setup a pgsql database inside docker
```
docker run -e "POSTGRES_DB=test" -p 5432:5432 --rm --name pgsql postgres:latest #(just ctrl-c to stop db and clean the container)
```
Start tests based on the database container
```
TEST_PGSQL_HOST=localhost:5432 TEST_PGSQL_DBNAME=test TEST_PGSQL_USERNAME=postgres TEST_PGSQL_PASSWORD=postgres make test-e2e-pgsql
```
## Run mssql e2e tests
Setup a mssql database inside docker
```
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_PID=Standard" -e "SA_PASSWORD=MwantsaSecurePassword1" -p 1433:1433 --rm --name mssql microsoft/mssql-server-linux:latest #(just ctrl-c to stop db and clean the container)
```
Start tests based on the database container
```
TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=gitea_test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-e2e-mssql
```
## Running individual tests
Example command to run `example.test.e2e.ts` test file:
_Note: unlike integration tests, this filtering is at the file level, not function_
For SQLite:
在项目根目录下:
```
make test-e2e-sqlite#example
make devstar
public/assets/install.sh clean # 清理已有的安装,警告:会删除已有全部数据!!!
public/assets/install.sh start --image=devstar-studio:latest
make e2e-test TARGET_URL="..." # 使用默认账号testuser 密码12345678
make e2e-test TARGET_URL="..." E2E_USERNAME="your_name" E2E_PASSWORD="your_password" # 使用已有的账号和密码
```
For other databases(replace `mssql` to `mysql` or `pgsql`):
* 通过make devstar 本地代码构建镜像devstar-studio:latest
* public/assets/install.sh start --image=devstar-studio:latest 脚本创建容器并输出devstar的URL比如http://192.168.234.210:80
* make e2e-test TARGET_URL="..."中输入devstar的URL如果首次安装会进入安装页面自动设置管理员账号密码如果已经安装过可以使用已有的账号密码否则按默认账号和密码登录。
* 注意URL不可以是localhost否则devcontainer容器及webterminal无法正常工作
```
TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-e2e-mssql#example
```
## 添加E2E端到端测试用例的方法
## Visual testing
* 所有的测试按照功能分组devcontainerappstorerunner等等每一个test函数对应一组或一个测试用例, 按照流程增加对应的测试用例和测试脚本。
Although the main goal of e2e is assertion testing, we have added a framework for visual regress testing. If you are working on front-end features, please use the following:
- Check out `main`, `make clean frontend`, and run e2e tests with `VISUAL_TEST=1` to generate outputs. This will initially fail, as no screenshots exist. You can run the e2e tests again to assert it passes.
- Check out your branch, `make clean frontend`, and run e2e tests with `VISUAL_TEST=1`. You should be able to assert you front-end changes don't break any other tests unintentionally.
举例说明如下:
VISUAL_TEST=1 will create screenshots in tests/e2e/test-snapshots. The test will fail the first time this is enabled (until we get visual test image persistence figured out), because it will be testing against an empty screenshot folder.
ACCEPT_VISUAL=1 will overwrite the snapshot images with new images.
todo

48
tests/e2e/global-setup.ts Normal file
repo.diff.view_file

@@ -0,0 +1,48 @@
import { chromium, type FullConfig } from '@playwright/test';
import { env } from 'node:process';
import { URL } from 'url';
async function globalSetup(config: FullConfig) {
const mode = env.E2E_MODE;
const {baseURL} = config.projects[0].use;
const isInstalledMode=env.E2E_SKIP_INSTALL;
const DEFAULT_E2E_USER = 'testuser';
const DEFAULT_E2E_PASS = '12345678';
if (!baseURL) {
throw new Error('[GlobalSetup] 致命错误: baseURL 或 storageState 未定义!');
}
const browser = await chromium.launch();
const context = await browser.newContext({
locale: 'zh-CN', // 强制中文
timezoneId: 'Asia/Shanghai', // 强制时区
});
const page = await context.newPage();
if (mode === 'url') {
try {
const url1=env.DEVSTAR_URL;
await page.goto(url1, { timeout: 15000 });
console.log('[GlobalSetup] 检测到安装界面!正在开始自动化安装...');
await page.getByText('服务器和第三方服务设置').click();
await page.getByRole('checkbox', { name: '启用通过 微信二维码 登录' }).uncheck();
await page.getByRole('checkbox', { name: '要求在用户注册时输入预验证码' }).uncheck();
await page.getByText('管理员帐号设置').click();
await page.getByRole('textbox', { name: '管理员用户名' }).fill('testuser');
await page.getByRole('textbox', { name: '邮箱地址' }).fill('ilovcatlyn750314@gmail.com');
await page.getByRole('textbox', { name: '管理员密码', exact: true }).fill('12345678');
await page.getByRole('textbox', { name: '确认密码' }).fill('12345678');
await page.getByRole('button', { name: '立即安装'}).click({ timeout: 10000, noWaitAfter: true });
console.log("安装中,请耐心等待");
await page.waitForTimeout(90000);
} catch (error) {
console.error('[GlobalSetup] "URL 模式" 登录失败:', error);
await page.screenshot({ path: 'playwright-report/global-setup-login-failure.png' });
throw error;
}
}
else {
throw new Error(`[GlobalSetup] 未知的 E2E_MODE: "${mode}"`);
}
await browser.close();
}
export default globalSetup;

20
tests/e2e/package.json Normal file
repo.diff.view_file

@@ -0,0 +1,20 @@
{
"name": "devstar-e2e-runner",
"version": "1.0.0",
"description": "Isolated E2E test runner for DevStar Studio",
"main": "index.js",
"scripts": {
"test": "npx playwright test"
},
"keywords": [
"playwright",
"e2e",
"devstar"
],
"author": "",
"license": "ISC",
"devDependencies": {
"@playwright/test": "1.53.2"
},
"dependencies": {}
}

126
tests/e2e/run-e2e-tests.sh Executable file
repo.diff.view_file

@@ -0,0 +1,126 @@
#!/bin/bash
# 这是一个“一键运行”E2E 测试的脚本
# 它会处理所有清理、权限、拉取和执行工作
# 任何命令失败立即退出
set -e
#基础配置与路径定义
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
PROJECT_ROOT=$( cd -- "$SCRIPT_DIR/../.." &> /dev/null && pwd )
export CURRENT_UID=$(id -u)
export CURRENT_GID=$(id -g)
cd "$PROJECT_ROOT"
echo "===== [1/3] 清理旧的测试环境... ====="
# 如果容器已存在,强制删除
if [ "$(docker ps -aq -f name=e2e-test-runner-container)" ]; then
docker rm -f e2e-test-runner-container
fi
# 清理悬空镜像
docker image prune -f
# 清理旧报告和数据,重建目录结构
rm -rf ./tests/e2e/reports ./tests/e2e/test-data
mkdir -p ./tests/e2e/reports/html ./tests/e2e/test-data/devstar_data
chmod -R 777 ./tests/e2e/reports
#这里添加的代码是因为需要执行npm install,我们以当前用户启动测试容器避免root权限冲突所以先预构建文件夹也作为缓存缓存npm install.
mkdir -p ./tests/e2e/node_modules
chmod 777 ./tests/e2e/node_modules
LOCK_FILE="./tests/e2e/package-lock.json"
# 确保 lock 文件存在且可写
if [ ! -f "$LOCK_FILE" ]; then
echo "{}" > "$LOCK_FILE"
fi
chmod 666 "$LOCK_FILE"
echo "===== [2/3] 准备环境变量... ====="
export DEVSTAR_URL=$TARGET_URL
export E2E_MODE="url"
if [ -n "$CI" ] || [ "$CI" = "true" ]; then
echo " [CI环境] 检测到 CI 环境,跳过 curl 安装状态检查..."
export E2E_SKIP_INSTALL="false"
else
echo " 正在检查安装状态..."
PATH_TO_CHECK="/user/login"
EXPECTED_CODE_IF_INSTALLED="200"
PROBE_URL="${TARGET_URL}${PATH_TO_CHECK}"
# 使用 curl 获取 HTTP 状态码
HTTP_CODE=$(curl -L -s -o /dev/null -w "%{http_code}" "$PROBE_URL")
if [ "$HTTP_CODE" -eq "$EXPECTED_CODE_IF_INSTALLED" ]; then
echo " 探测结果: 目标已安装 (在 ${PROBE_URL} 收到 HTTP ${HTTP_CODE})."
export E2E_SKIP_INSTALL="true"
else
echo " 探测结果: 目标未安装 (HTTP $HTTP_CODE),将执行安装脚本!"
export E2E_SKIP_INSTALL="false"
fi
fi
echo ""
echo "===== [3/3] 启动容器并运行测试... ====="
set +e
docker run -d --rm --init --ipc=host \
--name e2e-test-runner-container \
-u "root" \
-e DEVSTAR_URL="$DEVSTAR_URL" \
-e E2E_SKIP_INSTALL="$E2E_SKIP_INSTALL" \
-e E2E_USERNAME="$E2E_USERNAME" \
-e E2E_PASSWORD="$E2E_PASSWORD" \
-e E2E_MODE="$E2E_MODE" \
-e CI="$CI" \
-e npm_config_cache=/tmp/npm-cache \
-e HOME=/tmp \
-w /app \
-v /var/run/docker.sock:/var/run/docker.sock \
mcr.microsoft.com/playwright:v1.53.2-jammy \
tail -f /dev/null
echo "容器已启动,正在使用 docker cp 注入代码..."
# 注入代码文件
docker cp "$(pwd)/tests/e2e/package.json" e2e-test-runner-container:/app/package.json
docker cp "$(pwd)/playwright.config.ts" e2e-test-runner-container:/app/playwright.config.ts
docker cp "$(pwd)/tests/e2e/global-setup.ts" e2e-test-runner-container:/app/global-setup.ts
docker cp "$(pwd)/tests/e2e/specs" e2e-test-runner-container:/app/specs
# 在容器内执行安装与测试
docker exec e2e-test-runner-container bash -c "
# 确保 node_modules 目录存在
mkdir -p /app/node_modules
echo '正在安装依赖...'
npm install --no-package-lock
echo '依赖安装完成,开始测试...'
npx playwright test
"
EXIT_CODE=$?
set -e
# 导出测试报告
docker cp e2e-test-runner-container:/app/playwright-report/. tests/e2e/reports/html-report/
# 清理测试容器
docker rm -f e2e-test-runner-container
echo "========================================"
if [ $EXIT_CODE -eq 0 ]; then
echo "测试执行成功!"
else
echo "测试执行失败!"
fi
echo "========================================"
REPORT_DIR="./tests/e2e/reports/"
echo "HTML 报告已生成: $REPORT_DIR"
echo ""
exit $EXIT_CODE

repo.diff.view_file

@@ -0,0 +1,164 @@
import { test, expect } from '@playwright/test';
import { link } from 'node:fs';
import { env } from 'node:process';
import { Login } from './utils.e2e';
import { time } from 'node:console';
const DEFAULT_E2E_USER = 'testuser';
const DEFAULT_E2E_PASS = '12345678';
const DEFAULT_ADMIN_ID = '1';
const isAlreadyInstalled = env.E2E_SKIP_INSTALL === 'true';
const url1=env.DEVSTAR_URL;
const GITEA_URL = (env.E2E_MODE === 'url') ? url1 : 'http://devstar:3000';
const TEST_USER = isAlreadyInstalled ? (env.E2E_USERNAME || DEFAULT_E2E_USER) : DEFAULT_E2E_USER;
const TEST_PASS = isAlreadyInstalled ? (env.E2E_PASSWORD || DEFAULT_E2E_PASS) : DEFAULT_E2E_PASS;
const TEST_ADMIN_USER_ID = isAlreadyInstalled ? (env.E2E_ADMIN_ID || DEFAULT_ADMIN_ID) : DEFAULT_ADMIN_ID;
const repoName='e2e-test-repo';
test.describe('devcontainer 相关测试',()=>{
test.beforeEach(async ({ page }) => {
await Login(page);
});
test('DevContainer 功能和配置', async ({ page,context }) => {
console.log(`正在创建新仓库: ${repoName}`);
await page.goto(GITEA_URL + '/repo/create');
await page.fill('input[name="repo_name"]', repoName);
await page.getByRole('button', { name: '创建仓库' }).click();
await expect(page).toHaveURL(GITEA_URL + '/' + TEST_USER + '/' + repoName);
console.log("仓库创建成功.");
await expect(page).toHaveURL(GITEA_URL + '/' + TEST_USER + '/' + repoName);
console.log("正在点击 'Dev Container' 标签页...");
await page.getByRole('link', { name: '开发容器' }).click();
console.log("正在点击 'Create' (创建模板) 按钮...");
await page.getByRole('button', { name: /Create/i }).click();
await page.waitForTimeout(10000);
console.log("模板已创建. 正在点击 'Edit' 按钮...");
//await expect(page.getByText('devcontainer.json')).toBeVisible();
//转换为JSON 字符串
//const newJsonString = JSON.stringify(newJsonAsObject);
// 设置焦点
// await page.locator('.view-lines > div:nth-child(20)').click();
//console.log("正在手动删除模板内容 ");
//for (let i = 0; i < 500; i++) {
// await page.keyboard.press('Backspace');
// }
//粘贴字符串
//console.log("正在粘贴JSON 内容...");
// await page.keyboard.insertText(newJsonString);
//await page.getByRole('button', { name: 'Commit Changes' }).click();
//console.log("devcontainer.json 修改并提交成功.");
//console.log("正在导航回 Dev Container 标签页进行验证...");
//await page.getByRole('link', { name: '开发容器' }).click();
console.log("创建开发容器");
await page.getByRole('button', { name: '创建开发容器' }).click();
console.log("正在创建");
const stopButton = page.getByRole('button', { name: '停止开发容器' });
try {
console.log('正在等待容器创建');
//第一阶段刷新页面
await expect(stopButton).toBeVisible({ timeout: 60000 });
} catch (error) {
//刷新第一次
await page.reload({ waitUntil: 'domcontentloaded' });
console.log('60s正在刷新页面...');
//加载service可能比较慢
await page.waitForTimeout(180000);
console.log('刷新页面完成,继续等待 (阶段2: 60s)...');
//第二次刷新,容器应该正常启动了
await page.reload({ waitUntil: 'domcontentloaded' });
await expect(stopButton).toBeVisible({ timeout: 30000 });
await page.screenshot({ path: 'playwright-report/screenshot1.png', fullPage: true });
}
console.log("Dev container 创建成功!");
await page.getByRole('button',{ name: '停止开发容器'}).click();
console.log("正在停止");
await expect(page.getByRole('button',{ name: '启动开发容器'})).toBeVisible({ timeout: 180000});
console.log("成功停止开发容器!");
await page.getByRole('button',{name: '启动开发容器'}).click();
console.log("正在启动开发容器");
await expect(page.getByRole('button',{name:'停止开发容器'})).toBeVisible({ timeout: 180000});
console.log("成功启动!");
console.log("保存开发容器");
const pagePromise = context.waitForEvent('page');
console.log("打开webterminal");
await page.getByRole('link',{name: 'open with WebTerminal'}).click();
const newPage = await pagePromise;
await newPage.waitForLoadState(); // 等待新页面加载完成
await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForTimeout(30000);
await page.reload({ waitUntil: 'domcontentloaded' });
await page.screenshot({ path: 'playwright-report/screenshot2.png', fullPage: true });
console.log("Web Terminal: 新标签页已打开!");
//await expect(newPage.getByText('Successfully connected to the container')).toBeVisible(); //这里ttyd里的信息PlayWright看不见容器的交互没办法自动化测试
await page.getByRole('link', { name: '删除开发容器' }).click();
await page.locator('#delete-repo-devcontainer-of-user-modal')
.getByRole('button', { name: '确认操作' })
.click();
console.log('正在删除!,可能较慢请等待');
await page.waitForTimeout(3000);
await expect(page.getByRole('button', { name: '创建开发容器' })).toBeVisible();
console.log('成功删除!');
console.log("test1 检查通过!");
//console.log("test2仓库");
//await page.goto(GITEA_URL + '/'+ TEST_REPO_EMPTY);
//await page.getByRole('link', { name: 'Dev Container' }).click();
//await expect(page.getByText('Oops, it looks like there is no Dev Container Setting in this repository.')).toBeVisible();
//console.log("test2 检查通过");
//console.log("test3仓库");
//await page.goto(GITEA_URL+ '/' + TEST_REPO_INVALID);
//await page.getByRole('link', { name: 'Dev Container' }).click();
//await expect(page.getByText(' Invalid Dev Container Configuration')).toBeVisible();
//console.log("test3检查通过");
});
test('权限修改相关', async ({ page }) => {
console.log('权限配置');
await page.getByRole('link', { name: '管理后台' }).click();
await page.getByText('身份及认证').click();
await page.getByRole('link', { name: '帐户管理' }).click();
await page.getByRole('row')
.filter({ hasText: TEST_USER })
.getByRole('link', { name: '编辑' })
.click();
const devContainerCheckbox = page.getByLabel(/允许创建开发容器/i);
await devContainerCheckbox.uncheck();
await page.getByRole('button', { name: '更新帐户' }).click();
await page.goto(GITEA_URL + '/' + "e2e-devcontainer-test");
const devContainerLink = page.getByRole('link', { name: '开发容器' });
await expect(devContainerLink).toBeHidden({ timeout: 10000 });
console.log('权限设置成功!');
console.log('现在恢复原环境');
await page.goto(GITEA_URL+ '/-/admin/users/' + TEST_ADMIN_USER_ID + '/edit');
await devContainerCheckbox.check();
await page.getByRole('button', { name: '更新帐户' }).click();
console.log('现在清理测试仓库');
console.log("正在导航到仓库设置页面...");
await page.goto(GITEA_URL + '/' + TEST_USER + '/' + repoName + '/settings');
console.log("正在点击 'Delete This Repository' 按钮...");
await page.getByRole('button', { name: '删除本仓库' }).click();
await page.locator('#delete-repo-modal').waitFor();
console.log(`正在输入 '${repoName}' 进行确认...`);
await page.locator('#repo_name_to_delete').fill(repoName);
console.log("正在点击最终的删除确认按钮...");
await page.locator('#delete-repo-modal').getByRole('button', { name: '删除本仓库' }).click();
});
})

repo.diff.view_file

@@ -0,0 +1,68 @@
import { type Page, expect } from '@playwright/test';
import { env } from 'node:process';
const DEFAULT_E2E_USER = 'testuser';
const DEFAULT_E2E_PASS = '12345678';
const mode=env.E2E_MODE;
export async function Login(page: Page) {
const isInstalled = env.E2E_SKIP_INSTALL === 'true';
let username: string | undefined;
let password: string | undefined;
let wasUsingDefault = false;
if(mode === 'url'){
const url1=env.DEVSTAR_URL;
if (isInstalled) {
// "已安装" 模式
username = env.E2E_USERNAME || DEFAULT_E2E_USER;
password = env.E2E_PASSWORD || DEFAULT_E2E_PASS;
if (!env.E2E_USERNAME) {
wasUsingDefault = true;
}
console.log(`"已安装"模式, 尝试用 ${username} 登录...`);
} else {
// "未安装" 模式
username = DEFAULT_E2E_USER;
password = DEFAULT_E2E_PASS;
wasUsingDefault=true;
console.log(` "刚安装"模式, 尝试用 ${username} 登录...`);
}
try {
await page.goto(url1 + '/user/login');
const captchaInput = page.locator('input[name="验证码"]');
if (await captchaInput.isVisible()) {
throw new Error('检测到验证码 (CAPTCHA)! E2E 测试无法继续。');
}
await page.fill('#user_name',username);
await page.fill('#password', password);
await page.getByRole('button', { name: '登录' }).click();
await expect(page).toHaveURL(url1+ '/');
console.log(`[LoginHelper] 用户 [${username}] 登录成功!`);;
} catch (error) {
console.error(`[LoginHelper] 登录失败! 原始错误: ${error.message}`);
let hint: string;
if (error.message.includes('CAPTCHA')) {
// 提示 1: 验证码
hint = `请禁用验证码!\n` ;
} else if (wasUsingDefault) {
// 提示 2: 你没输入, 且默认值失败了
hint = `1. 登录失败, 且你没有提供 README.md 里描述的环境变量。\n` +
`2. 脚本自动尝试了默认用户 (${DEFAULT_E2E_USER}),但失败了。\n` +
`3. 请检查默认用户 (${DEFAULT_E2E_USER}) 在该目标上是否存在, 且密码是否正确。`;
} else {
hint = `1. 登录失败, 你提供了 E2E_USERNAME (${username})。\n` +
`2. 请检查你传入的 E2E_USERNAME 和 E2E_PASSWORD 环境变量是否正确。`;
}
throw new Error(
`[LoginHelper] 登录失败。\n\n${hint}\n`
);
}
}
}