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
|
||||
# 启动开发终端
|
||||
# 1. 启动开发终端
|
||||
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
|
||||
```
|
||||
|
||||
### 🤖 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",
|
||||
"scripts": {
|
||||
"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": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"build": {
|
||||
"appId": "cloud.zzzjy.hatty",
|
||||
"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": {
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/addon-search": "^0.16.0",
|
||||
@@ -21,6 +53,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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