把appimage应用转换为rpm包

目录

有很多linux上面的应用只提供appimage的单文件下载。这样用起来是方便了,但是管理起来就很麻烦,并且也没法修改应用的内容。于是尝试将appimage重新打包成rpm来用包管理器统一管理。

因为appimage本质上就是把应用和应用的依赖打包成一个单文件运行,所以只要把这些文件解压出来(appimage程序加–appimage-extract参数启动即可自解压),放到一个不会和其他程序冲突的地方,再在 $PATH 目录下创建对应的二进制的软链接和对应的 .desktop 文件就可以。

如果需要转换成deb包的话,操作应该差不多,只是描述文件会有所不同。

下面贴一下脚本,已经验证了 cursor, lmstudio, imhex 都可以正常转换至rpm并安装。

脚本使用 claude sonnet3.5 生成。不得不感叹 claude 的编程能力,需求描述清楚后一次就能生成正常运行的脚本。deepseek r1 还是差了点意思。

下载

appimage2rpm.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/bin/bash

# 检查参数
if [ $# -ne 1 ]; then
echo "用法: $0 <AppImage文件>"
exit 1
fi

APPIMAGE="$1"
WORKDIR="/tmp/appimage2rpm_$$" # 使用进程ID确保唯一性

# 检查AppImage文件是否存在
if [ ! -f "$APPIMAGE" ]; then
echo "错误: AppImage文件 '$APPIMAGE' 不存在"
exit 1
fi

# 创建工作目录
mkdir -p "$WORKDIR"
cd "$WORKDIR"

# 解压AppImage
echo "正在解压AppImage..."
chmod +x "$APPIMAGE"
"$APPIMAGE" --appimage-extract

# 获取应用信息
DESKTOP_FILE=$(find squashfs-root -name "*.desktop" | head -n 1)
if [ -z "$DESKTOP_FILE" ]; then
echo "错误: 未找到.desktop文件"
exit 1
fi

# 从desktop文件中读取应用信息
APP_NAME=$(grep "^Name=" "$DESKTOP_FILE" | head -n 1 | cut -d= -f2)
APP_VERSION=$(grep "^Version=" "$DESKTOP_FILE" | head -n 1 | cut -d= -f2)
if [ -z "$APP_VERSION" ]; then
APP_VERSION="1.0.0"
fi

# 在获取应用信息后添加图标处理
ICON_ENTRY=$(grep "^Icon=" "$DESKTOP_FILE" | head -n 1 | cut -d= -f2)
if [ -n "$ICON_ENTRY" ]; then
# 查找图标文件
ICON_FILE=""
# 按优先级查找不同格式的图标
for ext in ".png" ".svg" ".xpm"; do
# 首先检查完整路径
if [ -f "squashfs-root/$ICON_ENTRY$ext" ]; then
ICON_FILE="squashfs-root/$ICON_ENTRY$ext"
break
elif [ -f "squashfs-root/$ICON_ENTRY" ]; then
ICON_FILE="squashfs-root/$ICON_ENTRY"
break
fi

# 然后在常见位置查找
FOUND_ICON=$(find squashfs-root -type f -name "$ICON_ENTRY$ext" | head -n 1)
if [ -n "$FOUND_ICON" ]; then
ICON_FILE="$FOUND_ICON"
break
fi
done
fi

# 创建spec文件
# 处理应用名称:将空格替换为短横线,并转换为小写
SAFE_APP_NAME=$(echo "${APP_NAME,,}" | tr ' ' '-')

cat > "${APP_NAME}.spec" << EOF
Name: ${SAFE_APP_NAME}
Version: ${APP_VERSION}
Release: 1%{?dist}
Summary: ${APP_NAME} (AppImage转换)
License: Unknown
URL: Unknown

BuildArch: x86_64
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

%description
从AppImage转换的${APP_NAME}应用程序

%prep
# 无需解压源码

%build
# 无需构建

%install
rm -rf \$RPM_BUILD_ROOT
mkdir -p \$RPM_BUILD_ROOT/opt/%{name}
cp -r %{_sourcedir}/squashfs-root/* \$RPM_BUILD_ROOT/opt/%{name}/

# 创建必要的目录
mkdir -p \$RPM_BUILD_ROOT/usr/share/applications
mkdir -p \$RPM_BUILD_ROOT/usr/share/icons/hicolor/256x256/apps/
mkdir -p \$RPM_BUILD_ROOT/usr/share/icons/hicolor/scalable/apps/
mkdir -p \$RPM_BUILD_ROOT/usr/bin

# 创建软链接
ln -s /opt/%{name}/AppRun \$RPM_BUILD_ROOT/usr/bin/%{name}

# 处理图标
if [ -n "$ICON_FILE" ]; then
if [[ "$ICON_FILE" == *.svg ]]; then
cp %{_sourcedir}/"$ICON_FILE" \$RPM_BUILD_ROOT/usr/share/icons/hicolor/scalable/apps/%{name}.svg
ICON_PATH="%{name}"
else
cp %{_sourcedir}/"$ICON_FILE" \$RPM_BUILD_ROOT/usr/share/icons/hicolor/256x256/apps/%{name}.png
ICON_PATH="%{name}"
fi
fi

# 创建desktop文件
cat > \$RPM_BUILD_ROOT/usr/share/applications/%{name}.desktop << EOL
$(cat "$DESKTOP_FILE" | sed "s|Exec=.*|Exec=/opt/%{name}/AppRun|g" | sed "s|Icon=.*|Icon=%{name}|g")
EOL

%files
%defattr(-,root,root,-)
/opt/%{name}
/usr/share/applications/%{name}.desktop
/usr/share/icons/hicolor/*/apps/%{name}.*
/usr/bin/%{name}

%changelog
* $(date "+%a %b %d %Y") Builder <builder@localhost> - ${APP_VERSION}-1
- 从AppImage转换的初始RPM包
EOF

# 准备RPM构建环境
mkdir -p ~/rpmbuild/{SPECS,SOURCES}
cp "${APP_NAME}.spec" ~/rpmbuild/SPECS/
cp -r squashfs-root ~/rpmbuild/SOURCES/

# 构建RPM包
echo "正在构建RPM包..."
rpmbuild -ba ~/rpmbuild/SPECS/"${APP_NAME}.spec"

echo "完成!RPM包位于 ~/rpmbuild/RPMS/x86_64/ 目录"

# 清理工作目录
cd ..
rm -rf "$WORKDIR"