feat: add deb packaging and Gitea Actions automated release workflow
Build and Release / Build & Release DEB Package (push) Failing after 1m16s
Build and Release / Build & Release DEB Package (push) Failing after 1m16s
This commit is contained in:
@@ -0,0 +1,52 @@
|
|||||||
|
name: Build and Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag_name:
|
||||||
|
description: '发布版本标签 (如 v1.0.0)'
|
||||||
|
required: true
|
||||||
|
default: 'v1.0.0'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Build & Release DEB Package
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
|
- name: Install Linux build dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update || true
|
||||||
|
sudo apt-get install -y build-essential python3 libx11-dev libxext-dev libxrender-dev
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
npm ci || npm install
|
||||||
|
npm run rebuild
|
||||||
|
|
||||||
|
- name: Generate App Icons
|
||||||
|
run: |
|
||||||
|
python3 scripts/generate-icon.py
|
||||||
|
|
||||||
|
- name: Package Linux DEB
|
||||||
|
run: |
|
||||||
|
npm run package:deb
|
||||||
|
|
||||||
|
- name: Publish Release to Gitea
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GITHUB_SERVER_URL: ${{ github.server_url }}
|
||||||
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||||
|
GITHUB_REF_NAME: ${{ github.event.inputs.tag_name || github.ref_name }}
|
||||||
|
run: |
|
||||||
|
node scripts/gitea-release.mjs
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
name: Build and Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag_name:
|
||||||
|
description: '发布版本标签 (如 v1.0.0)'
|
||||||
|
required: true
|
||||||
|
default: 'v1.0.0'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Build & Release DEB Package
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
|
- name: Install Linux build dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update || true
|
||||||
|
sudo apt-get install -y build-essential python3 libx11-dev libxext-dev libxrender-dev
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
npm ci || npm install
|
||||||
|
npm run rebuild
|
||||||
|
|
||||||
|
- name: Generate App Icons
|
||||||
|
run: |
|
||||||
|
python3 scripts/generate-icon.py
|
||||||
|
|
||||||
|
- name: Package Linux DEB
|
||||||
|
run: |
|
||||||
|
npm run package:deb
|
||||||
|
|
||||||
|
- name: Publish Release to Gitea
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GITHUB_SERVER_URL: ${{ github.server_url }}
|
||||||
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||||
|
GITHUB_REF_NAME: ${{ github.event.inputs.tag_name || github.ref_name }}
|
||||||
|
run: |
|
||||||
|
node scripts/gitea-release.mjs
|
||||||
@@ -42,16 +42,27 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 启动与构建
|
## 🚀 启动与打包构建
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 启动开发终端
|
# 1. 启动开发终端
|
||||||
npm start
|
npm start
|
||||||
|
|
||||||
# 若 Node ABI 变动,重新编译底层 node-pty
|
# 2. 本地一键打包生成 Linux .deb 安装包 (生成于 dist/ 目录)
|
||||||
|
npm run package:deb
|
||||||
|
|
||||||
|
# 3. 安装生成的 deb 包
|
||||||
|
sudo dpkg -i dist/hatty_1.0.0_amd64.deb
|
||||||
|
|
||||||
|
# 4. 若 Node ABI 变动,重新编译底层 node-pty
|
||||||
npm run rebuild
|
npm run rebuild
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 🤖 Gitea Actions 自动化发版
|
||||||
|
项目已配置 Gitea Actions 流水线(`.gitea/workflows/release.yaml`):
|
||||||
|
- 推送以 `v` 开头的标签(如 `git tag v1.0.0 && git push origin v1.0.0`)会自动触发编译打包,并把 `.deb` 安装包作为附件发布到 Gitea Releases。
|
||||||
|
- 也可以在 Gitea 仓库页面中手动点击 **Actions -> Run workflow** 触发发布。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ 架构分层
|
## 🛠️ 架构分层
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Generated
+3043
-3
File diff suppressed because it is too large
Load Diff
+38
-5
@@ -5,11 +5,43 @@
|
|||||||
"main": "src/main/index.cjs",
|
"main": "src/main/index.cjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron .",
|
"start": "electron .",
|
||||||
"rebuild": "electron-rebuild -f -w node-pty"
|
"rebuild": "electron-rebuild -f -w node-pty",
|
||||||
|
"package:deb": "electron-builder --linux deb",
|
||||||
|
"package:all": "electron-builder --linux deb AppImage",
|
||||||
|
"dist": "electron-builder"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"build": {
|
||||||
"author": "",
|
"appId": "cloud.zzzjy.hatty",
|
||||||
"license": "ISC",
|
"productName": "hatty",
|
||||||
|
"directories": {
|
||||||
|
"output": "dist",
|
||||||
|
"buildResources": "build"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/**/*",
|
||||||
|
"node_modules/@xterm/**/*",
|
||||||
|
"node_modules/node-pty/**/*",
|
||||||
|
"package.json"
|
||||||
|
],
|
||||||
|
"linux": {
|
||||||
|
"target": [
|
||||||
|
"deb"
|
||||||
|
],
|
||||||
|
"category": "TerminalEmulator",
|
||||||
|
"maintainer": "zzzjy <zjy@zzzjy.cloud>",
|
||||||
|
"icon": "build/icons"
|
||||||
|
},
|
||||||
|
"deb": {
|
||||||
|
"packageName": "hatty",
|
||||||
|
"synopsis": "A modern, high-aesthetic Linux terminal emulator",
|
||||||
|
"description": "hatty is a fast, beautiful and modern terminal emulator for Linux built with Electron, WebGL and node-pty.",
|
||||||
|
"category": "Utility",
|
||||||
|
"priority": "optional"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"homepage": "https://git.zzzjy.cloud/zzzjy/hatty",
|
||||||
|
"author": "zzzjy <zjy@zzzjy.cloud>",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xterm/addon-fit": "^0.11.0",
|
"@xterm/addon-fit": "^0.11.0",
|
||||||
"@xterm/addon-search": "^0.16.0",
|
"@xterm/addon-search": "^0.16.0",
|
||||||
@@ -21,6 +53,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@electron/rebuild": "^4.2.0",
|
"@electron/rebuild": "^4.2.0",
|
||||||
"electron": "^44.4.3"
|
"electron": "^44.4.3",
|
||||||
|
"electron-builder": "^26.15.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import os
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
|
os.makedirs('build/icons', exist_ok=True)
|
||||||
|
|
||||||
|
size = (512, 512)
|
||||||
|
img = Image.new('RGBA', size, (0, 0, 0, 0))
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
|
# 1. 绘制带有圆角的深色背景外框 (Tokyo Night 磨砂质感)
|
||||||
|
# 圆角矩形
|
||||||
|
draw.rounded_rectangle([(16, 16), (496, 496)], radius=100, fill=(26, 27, 38, 255), outline=(122, 162, 247, 180), width=6)
|
||||||
|
|
||||||
|
# 2. 内部顶部标题栏装饰小圆点 (macOS / Linux 风格微缩)
|
||||||
|
draw.ellipse([(60, 60), (76, 76)], fill=(247, 118, 142, 255)) # 红
|
||||||
|
draw.ellipse([(92, 60), (108, 76)], fill=(224, 175, 104, 255)) # 黄
|
||||||
|
draw.ellipse([(124, 60), (140, 76)], fill=(158, 206, 106, 255)) # 绿
|
||||||
|
|
||||||
|
# 3. 绘制终端提示符 > (青色发光)
|
||||||
|
points = [
|
||||||
|
(110, 180),
|
||||||
|
(240, 290),
|
||||||
|
(110, 400),
|
||||||
|
(150, 400),
|
||||||
|
(280, 290),
|
||||||
|
(150, 180),
|
||||||
|
]
|
||||||
|
draw.polygon(points, fill=(125, 207, 255, 255))
|
||||||
|
|
||||||
|
# 4. 绘制光标下划线 _ (品红霓虹)
|
||||||
|
draw.rectangle([(300, 360), (410, 395)], fill=(187, 154, 247, 255))
|
||||||
|
|
||||||
|
# 保存 512x512 图标
|
||||||
|
img.save('build/icon.png', 'PNG')
|
||||||
|
img.save('build/icons/512x512.png', 'PNG')
|
||||||
|
|
||||||
|
# 同时也生成常见的 256x256, 128x128, 64x64, 32x32 备用
|
||||||
|
for s in [256, 128, 64, 32]:
|
||||||
|
resized = img.resize((s, s), Image.Resampling.LANCZOS)
|
||||||
|
resized.save(f'build/icons/{s}x{s}.png', 'PNG')
|
||||||
|
|
||||||
|
print('App icons generated successfully!')
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const serverUrl = process.env.GITHUB_SERVER_URL || process.env.GITEA_SERVER_URL || 'https://git.zzzjy.cloud';
|
||||||
|
const token = process.env.GITHUB_TOKEN || process.env.GITEA_TOKEN;
|
||||||
|
const repo = process.env.GITHUB_REPOSITORY || 'zzzjy/hatty';
|
||||||
|
const tag = process.env.GITHUB_REF_NAME || 'v1.0.0';
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
console.error('Error: GITHUB_TOKEN or GITEA_TOKEN is required.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [owner, repoName] = repo.split('/');
|
||||||
|
const apiBase = `${serverUrl.replace(/\/+$/, '')}/api/v1/repos/${owner}/${repoName}`;
|
||||||
|
|
||||||
|
console.log(`Connecting to Gitea at ${serverUrl} for repository ${repo}...`);
|
||||||
|
|
||||||
|
// 1. 检查或创建 Release
|
||||||
|
let releaseId = null;
|
||||||
|
const tagCheckRes = await fetch(`${apiBase}/releases/tags/${tag}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${token}`,
|
||||||
|
Accept: 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tagCheckRes.ok) {
|
||||||
|
const existing = await tagCheckRes.json();
|
||||||
|
releaseId = existing.id;
|
||||||
|
console.log(`Found existing release for tag ${tag} (id: ${releaseId})`);
|
||||||
|
} else {
|
||||||
|
console.log(`Creating new release for tag ${tag}...`);
|
||||||
|
const createRes = await fetch(`${apiBase}/releases`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${token}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Accept: 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
tag_name: tag,
|
||||||
|
name: `hatty ${tag}`,
|
||||||
|
body: `### ⚡ hatty ${tag} 现代化 Linux 终端新版本发布\n\n- **架构**:Electron + xterm.js (WebGL 硬件加速) + Linux PTY\n- **特性**:无边框磨砂毛玻璃、5 套现代暗黑主题、多标签页、灵活多窗格分屏\n- **安装说明**:下载下方附件 \`.deb\` 文件,在终端运行 \`sudo dpkg -i hatty_*.deb\` 即可完成安装。`,
|
||||||
|
draft: false,
|
||||||
|
prerelease: false,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!createRes.ok) {
|
||||||
|
const errText = await createRes.text();
|
||||||
|
console.error(`Failed to create release: ${createRes.status} ${errText}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const created = await createRes.json();
|
||||||
|
releaseId = created.id;
|
||||||
|
console.log(`Release created successfully with id ${releaseId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 查找 dist/ 下的 .deb 安装包
|
||||||
|
const distDir = path.resolve('dist');
|
||||||
|
if (!fs.existsSync(distDir)) {
|
||||||
|
console.error('dist directory does not exist.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = fs.readdirSync(distDir).filter((f) => f.endsWith('.deb') || f.endsWith('.AppImage'));
|
||||||
|
if (files.length === 0) {
|
||||||
|
console.error('No .deb or .AppImage files found in dist directory.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 上传安装包到 Release 附件
|
||||||
|
for (const fileName of files) {
|
||||||
|
const filePath = path.join(distDir, fileName);
|
||||||
|
const fileStat = fs.statSync(filePath);
|
||||||
|
console.log(`Uploading ${fileName} (${(fileStat.size / (1024 * 1024)).toFixed(2)} MB)...`);
|
||||||
|
|
||||||
|
const fileBuffer = fs.readFileSync(filePath);
|
||||||
|
const formData = new FormData();
|
||||||
|
const blob = new Blob([fileBuffer], { type: 'application/octet-stream' });
|
||||||
|
formData.append('attachment', blob, fileName);
|
||||||
|
|
||||||
|
const uploadRes = await fetch(`${apiBase}/releases/${releaseId}/assets?name=${encodeURIComponent(fileName)}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${token}`,
|
||||||
|
},
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (uploadRes.ok) {
|
||||||
|
console.log(`✔ Successfully uploaded ${fileName}`);
|
||||||
|
} else {
|
||||||
|
const err = await uploadRes.text();
|
||||||
|
console.warn(`Warning: Upload ${fileName} returned status ${uploadRes.status}: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('All release operations completed successfully!');
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error('Unexpected error:', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user