<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: viraj-sh</title>
    <description>The latest articles on DEV Community by viraj-sh (@virajsh).</description>
    <link>https://dev.to/virajsh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3534927%2F8774ab63-70c9-4b1f-b74a-6c5907b5fb5a.png</url>
      <title>DEV Community: viraj-sh</title>
      <link>https://dev.to/virajsh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/virajsh"/>
    <language>en</language>
    <item>
      <title>Create .deb package of python project</title>
      <dc:creator>viraj-sh</dc:creator>
      <pubDate>Fri, 06 Nov 2026 19:03:49 +0000</pubDate>
      <link>https://dev.to/virajsh/create-deb-package-of-python-project-2cep</link>
      <guid>https://dev.to/virajsh/create-deb-package-of-python-project-2cep</guid>
      <description>&lt;p&gt;You're right—my previous answer assumed you already had an &lt;code&gt;AppDir&lt;/code&gt;. Since you want to start completely from scratch, the most straightforward path is to &lt;strong&gt;reuse the exact same self-contained runtime construction logic from your AppImage tutorial&lt;/strong&gt;, but instead of packaging the result into an AppImage, directly place that runtime directory into the &lt;code&gt;.deb&lt;/code&gt; file structure.&lt;/p&gt;

&lt;p&gt;The core idea is: &lt;strong&gt;build the Python environment first, then package it with &lt;code&gt;dpkg-deb&lt;/code&gt;&lt;/strong&gt;. Below are the complete steps.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1: Install build tools and runtime dependencies
&lt;/h3&gt;

&lt;p&gt;In Ubuntu, you need &lt;code&gt;dpkg-deb&lt;/code&gt; (used to package &lt;code&gt;.deb&lt;/code&gt;), &lt;code&gt;uv&lt;/code&gt; (used to manage Python and build wheels), and some basic system libraries (for Qt/WebEngine runtime).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; dpkg-dev fakeroot curl patchelf file libgl1 libglib2.0-0 libfontconfig1 libx11-6 libxkbcommon0 libdbus-1-3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install &lt;code&gt;uv&lt;/code&gt; (if not already installed):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://astral.sh/uv/install.sh | sh
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.local/bin/env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2: Prepare a self-contained Python runtime (completely from scratch)
&lt;/h3&gt;

&lt;p&gt;This step replicates the runtime construction logic from your AppImage tutorial, but installs into a local directory instead of &lt;code&gt;AppDir&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"uniclare-client"&lt;/span&gt;
&lt;span class="nv"&gt;VER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"3.0.0"&lt;/span&gt;
&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"amd64"&lt;/span&gt;
&lt;span class="nv"&gt;RUNTIME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"runtime"&lt;/span&gt;   &lt;span class="c"&gt;# 替代 AppDir，用来存放自包含运行时&lt;/span&gt;

&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RUNTIME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RUNTIME&lt;/span&gt;&lt;span class="s2"&gt;/usr/lib/unidesk"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Get the Python interpreter&lt;/strong&gt; (using the Python 3.12 managed by &lt;code&gt;uv&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PY_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;readlink&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;uv python find 3.12 &lt;span class="nt"&gt;--no-project&lt;/span&gt;&lt;span class="si"&gt;))))&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"PY_ROOT=&lt;/span&gt;&lt;span class="nv"&gt;$PY_ROOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PY_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/bin/python3"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"OK: interpreter exists"&lt;/span&gt;

&lt;span class="c"&gt;# 复制解释器到运行时目录&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PY_ROOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/. &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RUNTIME&lt;/span&gt;&lt;span class="s2"&gt;/usr/lib/unidesk/python/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Build the project wheel and dependency wheels&lt;/strong&gt; (assuming you're at the project root and have &lt;code&gt;pyproject.toml&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv build &lt;span class="nt"&gt;--wheel&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; build/wheels

&lt;span class="c"&gt;# 如果项目依赖中有 PyPI 上只有 sdist 的包（如 proxy-tools），按你原教程的方式构建 wheel&lt;/span&gt;
&lt;span class="c"&gt;# uv build --wheel --out-dir build/wheels build/wheels/proxy_tools-0.1.0.tar.gz&lt;/span&gt;

&lt;span class="c"&gt;# 导出锁定依赖（不含项目自身）&lt;/span&gt;
uv &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nt"&gt;--frozen&lt;/span&gt; &lt;span class="nt"&gt;--no-dev&lt;/span&gt; &lt;span class="nt"&gt;--no-emit-project&lt;/span&gt; &lt;span class="nt"&gt;--extra&lt;/span&gt; desktop &lt;span class="nt"&gt;--no-hashes&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; build/requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Install all Python dependencies into the runtime&lt;/strong&gt; (using the just-copied interpreter, installing to &lt;code&gt;site-packages&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--python&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RUNTIME&lt;/span&gt;&lt;span class="s2"&gt;/usr/lib/unidesk/python/bin/python3"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RUNTIME&lt;/span&gt;&lt;span class="s2"&gt;/usr/lib/unidesk/site-packages"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-deps&lt;/span&gt; &lt;span class="nt"&gt;--no-build&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--find-links&lt;/span&gt; build/wheels &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-r&lt;/span&gt; build/requirements.txt dist/uniclare_client-3.0.0-py3-none-any.whl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;runtime/usr/lib/unidesk/&lt;/code&gt; contains the Python interpreter and all Python packages (including PyQt6 and WebEngine).&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: (Strongly recommended) Prune the runtime
&lt;/h3&gt;

&lt;p&gt;If you skip this step, the &lt;code&gt;.deb&lt;/code&gt; will be very large (your AppImage tutorial mentions it can be reduced from 651M to around 400–450M). The pruning script from your original tutorial can be used directly, just change &lt;code&gt;U=AppDir/...&lt;/code&gt; to &lt;code&gt;U=runtime/usr/lib/unidesk/...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;prune.sh&lt;/code&gt; (content based on your AppImage tutorial, replacing paths):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; prune.sh &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PRUNE&lt;/span&gt;&lt;span class="sh"&gt;'
set -e
U=runtime/usr/lib/unidesk
PYLIB=&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/python/lib/python3.12
SP=&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/site-packages
QT=&lt;/span&gt;&lt;span class="nv"&gt;$SP&lt;/span&gt;&lt;span class="sh"&gt;/PyQt6/Qt6
QTLIB=&lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="sh"&gt;/lib

# A. 精简内置 Python
rm -rf &lt;/span&gt;&lt;span class="nv"&gt;$PYLIB&lt;/span&gt;&lt;span class="sh"&gt;/{test,idlelib,tkinter,turtledemo,lib2to3,ensurepip,pydoc_data}
rm -f  &lt;/span&gt;&lt;span class="nv"&gt;$PYLIB&lt;/span&gt;&lt;span class="sh"&gt;/turtle.py
rm -f  &lt;/span&gt;&lt;span class="nv"&gt;$PYLIB&lt;/span&gt;&lt;span class="sh"&gt;/lib-dynload/_tkinter*.so &lt;/span&gt;&lt;span class="nv"&gt;$PYLIB&lt;/span&gt;&lt;span class="sh"&gt;/lib-dynload/_test*.so &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
       &lt;/span&gt;&lt;span class="nv"&gt;$PYLIB&lt;/span&gt;&lt;span class="sh"&gt;/lib-dynload/_ctypes_test*.so &lt;/span&gt;&lt;span class="nv"&gt;$PYLIB&lt;/span&gt;&lt;span class="sh"&gt;/lib-dynload/xxlimited*.so
rm -rf &lt;/span&gt;&lt;span class="nv"&gt;$PYLIB&lt;/span&gt;&lt;span class="sh"&gt;/site-packages/pip*
rm -rf &lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/python/{include,share}
rm -rf &lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/python/lib/{tcl*,tk*,itcl*,thread*}
rm -f  &lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/python/lib/libtcl* &lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/python/lib/libtk*
rm -f  &lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/python/bin/{pip*,idle*,pydoc*,2to3*,python3.12-config}
find &lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/python -name "*.a" -delete

# B. 通用 site-packages 清理
find &lt;/span&gt;&lt;span class="nv"&gt;$SP&lt;/span&gt;&lt;span class="sh"&gt; -type d -name tests -prune -exec rm -rf {} +
find &lt;/span&gt;&lt;span class="nv"&gt;$SP&lt;/span&gt;&lt;span class="sh"&gt; -name "*.pyi" -delete
rm -rf &lt;/span&gt;&lt;span class="nv"&gt;$SP&lt;/span&gt;&lt;span class="sh"&gt;/PyQt6/bindings &lt;/span&gt;&lt;span class="nv"&gt;$SP&lt;/span&gt;&lt;span class="sh"&gt;/PyQt6/uic/widget-plugins

# C. Qt 翻译（只保留 WebEngine en-US）
find &lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="sh"&gt;/translations -type f ! -path '*qtwebengine_locales/en-US.pak' -delete
find &lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="sh"&gt;/translations -type d -empty -delete
rm -f &lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="sh"&gt;/resources/qtwebengine_devtools_resources.pak
rm -rf &lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="sh"&gt;/qml &lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="sh"&gt;/bin

# D. Qt 插件裁剪
rm -rf &lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="sh"&gt;/plugins/{designer,sqldrivers,multimedia,qmltooling,sensors,position,geoservices,texttospeech,qmllint}

# E. 精简 PyQt6 绑定
KEEP=" QtCore QtGui QtWidgets QtNetwork QtWebChannel QtWebEngineCore QtWebEngineWidgets QtPrintSupport QtDBus QtOpenGL QtOpenGLWidgets QtSvg "
for f in &lt;/span&gt;&lt;span class="nv"&gt;$SP&lt;/span&gt;&lt;span class="sh"&gt;/PyQt6/*.abi3.so; do
  m=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;; m=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;m&lt;/span&gt;&lt;span class="p"&gt;%%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
  case "&lt;/span&gt;&lt;span class="nv"&gt;$KEEP&lt;/span&gt;&lt;span class="sh"&gt;" in *" &lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="sh"&gt; "*) ;; *) rm -f "&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="sh"&gt;" "&lt;/span&gt;&lt;span class="nv"&gt;$SP&lt;/span&gt;&lt;span class="sh"&gt;/PyQt6/&lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="sh"&gt;.pyi" ;; esac
done

# F. 依赖闭包删除无用 Qt C++ 库
declare -A need
queue=( &lt;/span&gt;&lt;span class="nv"&gt;$SP&lt;/span&gt;&lt;span class="sh"&gt;/PyQt6/*.abi3.so "&lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="sh"&gt;/libexec/QtWebEngineProcess" )
while IFS= read -r p; do queue+=("&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="sh"&gt;"); done &amp;lt; &amp;lt;(find "&lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="sh"&gt;/plugins" -name '*.so')
while ((&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;)); do
  f=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;; queue=("&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;:1&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;")
  for n in &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;patchelf &lt;span class="nt"&gt;--print-needed&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'^libQt6'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;; do
    b=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;%%.so*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
    [[ -n &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;need&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="sh"&gt; ]] &amp;amp;&amp;amp; continue
    need[&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="sh"&gt;]=1
    [[ -e &lt;/span&gt;&lt;span class="nv"&gt;$QTLIB&lt;/span&gt;&lt;span class="sh"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="sh"&gt; ]] &amp;amp;&amp;amp; queue+=("&lt;/span&gt;&lt;span class="nv"&gt;$QTLIB&lt;/span&gt;&lt;span class="sh"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="sh"&gt;")
  done
done
for f in &lt;/span&gt;&lt;span class="nv"&gt;$QTLIB&lt;/span&gt;&lt;span class="sh"&gt;/libQt6*; do
  b=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;; b=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="p"&gt;%%.so*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
  [[ -z &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;need&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="sh"&gt; ]] &amp;amp;&amp;amp; rm -f "&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="sh"&gt;"
done

# G. 权限
find runtime -name "QtWebEngineProcess" -exec chmod +x {} &lt;/span&gt;&lt;span class="se"&gt;\;&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;PRUNE

&lt;/span&gt;bash prune.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 4: Create the AppRun launcher (inside the runtime directory)
&lt;/h3&gt;

&lt;p&gt;This is the entry point for the runtime; &lt;code&gt;/usr/bin/uniclare-client&lt;/code&gt; will call it later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RUNTIME&lt;/span&gt;&lt;span class="s2"&gt;/AppRun"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
#!/bin/sh
HERE="&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;readlink&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"
U="&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="sh"&gt;/usr/lib/unidesk"
unset PYTHONHOME PYTHONSTARTUP
export PYTHONPATH="&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/site-packages"
export PYTHONNOUSERSITE=1 PYTHONDONTWRITEBYTECODE=1 PYTHONSAFEPATH=1
export LD_LIBRARY_PATH="&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="sh"&gt;/usr/lib:&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="sh"&gt;/usr/lib/x86_64-linux-gnu:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"
export PYWEBVIEW_GUI=qt QT_API=pyqt6 QTWEBENGINE_DISABLE_SANDBOX=1
export QTWEBENGINEPROCESS_PATH="&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/site-packages/PyQt6/Qt6/libexec/QtWebEngineProcess"
export QTWEBENGINE_RESOURCES_PATH="&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/site-packages/PyQt6/Qt6/resources"
exec "&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/python/bin/python3" -m uniclare_client.interfaces.desktop "&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="sh"&gt;"
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RUNTIME&lt;/span&gt;&lt;span class="s2"&gt;/AppRun"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test that the runtime starts properly (before packaging):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./runtime/AppRun
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 5: Build the &lt;code&gt;.deb&lt;/code&gt; directory structure
&lt;/h3&gt;

&lt;p&gt;Now create the Debian package structure and move the runtime in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"uniclare-client"&lt;/span&gt;
&lt;span class="nv"&gt;VER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"3.0.0"&lt;/span&gt;
&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"amd64"&lt;/span&gt;
&lt;span class="nv"&gt;APP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"io.github.viraj_sh.uniclare_client"&lt;/span&gt;

&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; deb-build
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/DEBIAN
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/opt/&lt;span class="nv"&gt;$PKG&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/usr/bin
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/usr/share/applications
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/usr/share/icons/hicolor/scalable/apps
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/usr/share/metainfo

&lt;span class="c"&gt;# 把整个 runtime 放进 /opt/uniclare-client/&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; runtime/. deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/opt/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the runtime inside the &lt;code&gt;.deb&lt;/code&gt; is located at &lt;code&gt;/opt/uniclare-client/&lt;/code&gt;, and its &lt;code&gt;AppRun&lt;/code&gt; is at &lt;code&gt;/opt/uniclare-client/AppRun&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 6: Add desktop integration files
&lt;/h3&gt;

&lt;p&gt;Use the &lt;code&gt;.desktop&lt;/code&gt;, &lt;code&gt;.svg&lt;/code&gt;, and &lt;code&gt;.metainfo.xml&lt;/code&gt; from your project (paths based on your original tutorial):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-Dm644&lt;/span&gt; &lt;span class="s2"&gt;"packaging/appimage/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.desktop"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"deb-build/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;/usr/share/applications/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.desktop"&lt;/span&gt;
&lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-Dm644&lt;/span&gt; &lt;span class="s2"&gt;"packaging/appimage/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.svg"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"deb-build/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;/usr/share/icons/hicolor/scalable/apps/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.svg"&lt;/span&gt;
&lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-Dm644&lt;/span&gt; &lt;span class="s2"&gt;"packaging/appimage/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.metainfo.xml"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"deb-build/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;/usr/share/metainfo/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.metainfo.xml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify the &lt;code&gt;Exec&lt;/code&gt; line in the &lt;code&gt;.desktop&lt;/code&gt; file to point to the Debian package's startup command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s|^Exec=.*|Exec=/usr/bin/uniclare-client|'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"deb-build/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;/usr/share/applications/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.desktop"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 7: Create the &lt;code&gt;/usr/bin&lt;/code&gt; launcher
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"deb-build/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;/usr/bin/uniclare-client"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
#!/bin/sh
exec /opt/uniclare-client/AppRun "&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="sh"&gt;"
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;755 &lt;span class="s2"&gt;"deb-build/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;/usr/bin/uniclare-client"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 8: Write the &lt;code&gt;DEBIAN/control&lt;/code&gt; file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"deb-build/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;/DEBIAN/control"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
Package: &lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="sh"&gt;
Version: &lt;/span&gt;&lt;span class="nv"&gt;$VER&lt;/span&gt;&lt;span class="sh"&gt;
Section: utils
Priority: optional
Architecture: &lt;/span&gt;&lt;span class="nv"&gt;$ARCH&lt;/span&gt;&lt;span class="sh"&gt;
Maintainer: viraj-sh &amp;lt;viraj-sh@users.noreply.github.com&amp;gt;
Depends: libc6, libgl1, libglib2.0-0, libfontconfig1, libx11-6, libxkbcommon0, libdbus-1-3
Description: Uniclare Client
 View data that is hidden on the Uniclare App or Student Portal.
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You &lt;strong&gt;do not&lt;/strong&gt; need to list &lt;code&gt;python3&lt;/code&gt; or &lt;code&gt;python3-pyqt6&lt;/code&gt; in &lt;code&gt;Depends&lt;/code&gt; because you've bundled Python and PyQt6 yourself, so you don't rely on system packages. The listed system libraries are the basic dependencies needed for Qt/WebEngine to run, and they are present on virtually every Ubuntu desktop system.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 9: Set permissions and build
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;755 deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/DEBIAN
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/DEBIAN/control
find deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;755 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;755 deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt;/usr/bin/uniclare-client

dpkg-deb &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nt"&gt;--root-owner-group&lt;/span&gt; deb-build/&lt;span class="nv"&gt;$PKG&lt;/span&gt; &lt;span class="s2"&gt;"uniclare-client_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.deb"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 10: Install and test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"uniclare-client_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.deb"&lt;/span&gt;
&lt;span class="c"&gt;# 如果有依赖缺失：&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="c"&gt;# 运行&lt;/span&gt;
uniclare-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should also be able to find and launch it from the application menu.&lt;/p&gt;

&lt;p&gt;Uninstall:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;-r&lt;/span&gt; uniclare-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Key differences from your original tutorial
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;AppImage tutorial&lt;/th&gt;
&lt;th&gt;This &lt;code&gt;.deb&lt;/code&gt; procedure&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runtime location&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AppDir/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;runtime/&lt;/code&gt; (only a name change)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime placement in package&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/&lt;/code&gt; (AppImage root)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/opt/uniclare-client/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entry point&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AppRun&lt;/code&gt; executed directly&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AppRun&lt;/code&gt; called via &lt;code&gt;/usr/bin/uniclare-client&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packaging tool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;appimagetool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dpkg-deb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The core "from scratch building Python + PyQt6 + WebEngine runtime" logic is completely identical to your AppImage tutorial; only the final packaging container is different. As long as &lt;code&gt;./runtime/AppRun&lt;/code&gt; can start the app normally, the &lt;code&gt;.deb&lt;/code&gt; will also work normally.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>linux</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Create App image of python desktop projects</title>
      <dc:creator>viraj-sh</dc:creator>
      <pubDate>Fri, 06 Nov 2026 18:27:11 +0000</pubDate>
      <link>https://dev.to/virajsh/create-app-image-of-python-desktop-projects-3go4</link>
      <guid>https://dev.to/virajsh/create-app-image-of-python-desktop-projects-3go4</guid>
      <description>&lt;h3&gt;
  
  
  1. Install required tools
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git patchelf desktop-file-utils appstream file nodejs npm libfuse2t64 curl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make both appimages executable&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x linuxdeploy-x86_64.AppImage appimagetool-x86_64.AppImage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Wheel
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv build &lt;span class="nt"&gt;--wheel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; build/wheels
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; build/wheels/proxy_tools-0.1.0.tar.gz &lt;span class="se"&gt;\&lt;/span&gt;
  https://pypi.org/packages/source/p/proxy-tools/proxy_tools-0.1.0.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; build/wheels/proxy_tools-0.1.0.tar.gz
file build/wheels/proxy_tools-0.1.0.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  proxy-tools has no wheel on PyPI; build one from sdist so --no-build later works
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv build &lt;span class="nt"&gt;--wheel&lt;/span&gt; &lt;span class="nt"&gt;--out-dir&lt;/span&gt; build/wheels build/wheels/proxy_tools-0.1.0.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify static dir is present inside wheel&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;unzip &lt;span class="nt"&gt;-l&lt;/span&gt; dist/&lt;span class="k"&gt;*&lt;/span&gt;.whl | &lt;span class="nb"&gt;grep &lt;/span&gt;static
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Export Locked requirements&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; build
uv &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nt"&gt;--frozen&lt;/span&gt; &lt;span class="nt"&gt;--no-dev&lt;/span&gt; &lt;span class="nt"&gt;--no-emit-project&lt;/span&gt; &lt;span class="nt"&gt;--extra&lt;/span&gt; desktop &lt;span class="nt"&gt;--no-hashes&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; build/requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. uild the bundled runtime
&lt;/h3&gt;

&lt;p&gt;Assemble the AppDir Layout&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;APP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"io.github.viraj_sh.uniclare_client"&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; AppDir/usr/share/applications
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; AppDir/usr/share/icons/hicolor/scalable/apps
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; AppDir/usr/share/metainfo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy .desktop .metainfo.xml and .svg in the AppDir&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .desktop → root AND usr/share/applications/&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"packaging/appimage/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.desktop"&lt;/span&gt; &lt;span class="s2"&gt;"AppDir/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.desktop"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"packaging/appimage/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.desktop"&lt;/span&gt; &lt;span class="s2"&gt;"AppDir/usr/share/applications/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.desktop"&lt;/span&gt;

&lt;span class="c"&gt;# .svg → root AND usr/share/icons/hicolor/scalable/apps/&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"packaging/appimage/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.svg"&lt;/span&gt; &lt;span class="s2"&gt;"AppDir/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.svg"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"packaging/appimage/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.svg"&lt;/span&gt; &lt;span class="s2"&gt;"AppDir/usr/share/icons/hicolor/scalable/apps/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.svg"&lt;/span&gt;

&lt;span class="c"&gt;# metainfo → usr/share/metainfo/ only&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"packaging/appimage/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.metainfo.xml"&lt;/span&gt; &lt;span class="s2"&gt;"AppDir/usr/share/metainfo/&lt;/span&gt;&lt;span class="nv"&gt;$APP_ID&lt;/span&gt;&lt;span class="s2"&gt;.metainfo.xml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create AppRun&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; AppDir/AppRun &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
#!/bin/sh
HERE="&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;readlink&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"
U="&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="sh"&gt;/usr/lib/unidesk"
unset PYTHONHOME PYTHONSTARTUP
export PYTHONPATH="&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/site-packages"
export PYTHONNOUSERSITE=1 PYTHONDONTWRITEBYTECODE=1 PYTHONSAFEPATH=1
export LD_LIBRARY_PATH="&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="sh"&gt;/usr/lib:&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="sh"&gt;/usr/lib/x86_64-linux-gnu:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"
export PYWEBVIEW_GUI=qt QT_API=pyqt6 QTWEBENGINE_DISABLE_SANDBOX=1
export QTWEBENGINEPROCESS_PATH="&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/site-packages/PyQt6/Qt6/libexec/QtWebEngineProcess"
export QTWEBENGINE_RESOURCES_PATH="&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/site-packages/PyQt6/Qt6/resources"
exec "&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;&lt;span class="sh"&gt;/python/bin/python3" -m uniclare_client.interfaces.desktop "&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="sh"&gt;"
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x AppDir/AppRun
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy interpreter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PY_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;readlink&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;uv python find 3.12 &lt;span class="nt"&gt;--no-project&lt;/span&gt;&lt;span class="si"&gt;))))&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"PY_ROOT=&lt;/span&gt;&lt;span class="nv"&gt;$PY_ROOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PY_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/bin/python3"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"OK: interpreter exists"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; AppDir/usr/lib/unidesk/python
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PY_ROOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/. AppDir/usr/lib/unidesk/python/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--python&lt;/span&gt; AppDir/usr/lib/unidesk/python/bin/python3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target&lt;/span&gt; AppDir/usr/lib/unidesk/site-packages &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-deps&lt;/span&gt; &lt;span class="nt"&gt;--no-build&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--find-links&lt;/span&gt; build/wheels &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-r&lt;/span&gt; build/requirements.txt dist/uniclare_client-3.0.0-py3-none-any.whl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Fix QtWebEngineProcess permissions (required, otherwise white screen)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find AppDir &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"QtWebEngineProcess"&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Package AppImage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./appimagetool-x86_64.AppImage AppDir uniclare_client.AppImage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Steps 1-3 stay exactly as they are. Replace steps 4 and 5 with the ones below. Your &lt;code&gt;du&lt;/code&gt; output shows the weight is in &lt;code&gt;site-packages&lt;/code&gt; (540M, which is PyQt6 and the Chromium engine) and the bundled Python (111M, which carries tests, tcl/tk, static libs and pip).&lt;/p&gt;

&lt;p&gt;One correction to my earlier script: it deleted the whole &lt;code&gt;Qt6/translations&lt;/code&gt; folder, and that folder holds &lt;code&gt;qtwebengine_locales&lt;/code&gt;. That could have caused the white screen again, so the version below keeps &lt;code&gt;en-US.pak&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Prune the AppDir (new step, save as &lt;code&gt;prune.sh&lt;/code&gt; and run &lt;code&gt;bash prune.sh&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nv"&gt;U&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AppDir/usr/lib/unidesk
&lt;span class="nv"&gt;PYLIB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;/python/lib/python3.12
&lt;span class="nv"&gt;SP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$U&lt;/span&gt;/site-packages
&lt;span class="nv"&gt;QT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$SP&lt;/span&gt;/PyQt6/Qt6
&lt;span class="nv"&gt;QTLIB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;/lib

&lt;span class="c"&gt;############ A. Bundled Python ############&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$PYLIB&lt;/span&gt;/&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;,idlelib,tkinter,turtledemo,lib2to3,ensurepip,pydoc_data&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;  &lt;span class="nv"&gt;$PYLIB&lt;/span&gt;/turtle.py
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;  &lt;span class="nv"&gt;$PYLIB&lt;/span&gt;/lib-dynload/_tkinter&lt;span class="k"&gt;*&lt;/span&gt;.so &lt;span class="nv"&gt;$PYLIB&lt;/span&gt;/lib-dynload/_test&lt;span class="k"&gt;*&lt;/span&gt;.so &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nv"&gt;$PYLIB&lt;/span&gt;/lib-dynload/_ctypes_test&lt;span class="k"&gt;*&lt;/span&gt;.so &lt;span class="nv"&gt;$PYLIB&lt;/span&gt;/lib-dynload/xxlimited&lt;span class="k"&gt;*&lt;/span&gt;.so
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$PYLIB&lt;/span&gt;/site-packages/pip&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$U&lt;/span&gt;/python/&lt;span class="o"&gt;{&lt;/span&gt;include,share&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$U&lt;/span&gt;/python/lib/&lt;span class="o"&gt;{&lt;/span&gt;tcl&lt;span class="k"&gt;*&lt;/span&gt;,tk&lt;span class="k"&gt;*&lt;/span&gt;,itcl&lt;span class="k"&gt;*&lt;/span&gt;,thread&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;  &lt;span class="nv"&gt;$U&lt;/span&gt;/python/lib/libtcl&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$U&lt;/span&gt;/python/lib/libtk&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;  &lt;span class="nv"&gt;$U&lt;/span&gt;/python/bin/&lt;span class="o"&gt;{&lt;/span&gt;pip&lt;span class="k"&gt;*&lt;/span&gt;,idle&lt;span class="k"&gt;*&lt;/span&gt;,pydoc&lt;span class="k"&gt;*&lt;/span&gt;,2to3&lt;span class="k"&gt;*&lt;/span&gt;,python3.12-config&lt;span class="o"&gt;}&lt;/span&gt;
find &lt;span class="nv"&gt;$U&lt;/span&gt;/python &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.a"&lt;/span&gt; &lt;span class="nt"&gt;-delete&lt;/span&gt;

&lt;span class="c"&gt;############ B. Generic site-packages junk ############&lt;/span&gt;
find &lt;span class="nv"&gt;$SP&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-name&lt;/span&gt; tests &lt;span class="nt"&gt;-prune&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; +
find &lt;span class="nv"&gt;$SP&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.pyi"&lt;/span&gt; &lt;span class="nt"&gt;-delete&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$SP&lt;/span&gt;/PyQt6/bindings &lt;span class="nv"&gt;$SP&lt;/span&gt;/PyQt6/uic/widget-plugins

&lt;span class="c"&gt;############ C. Qt: translations (keep only the WebEngine en-US locale) ############&lt;/span&gt;
find &lt;span class="nv"&gt;$QT&lt;/span&gt;/translations &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt; &lt;span class="s1"&gt;'*qtwebengine_locales/en-US.pak'&lt;/span&gt; &lt;span class="nt"&gt;-delete&lt;/span&gt;
find &lt;span class="nv"&gt;$QT&lt;/span&gt;/translations &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-empty&lt;/span&gt; &lt;span class="nt"&gt;-delete&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;$QT&lt;/span&gt;/resources/qtwebengine_devtools_resources.pak   &lt;span class="c"&gt;# only needed for debug=True / remote devtools&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$QT&lt;/span&gt;/qml &lt;span class="nv"&gt;$QT&lt;/span&gt;/bin

&lt;span class="c"&gt;############ D. Qt: plugins you don't need ############&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$QT&lt;/span&gt;/plugins/&lt;span class="o"&gt;{&lt;/span&gt;designer,sqldrivers,multimedia,qmltooling,sensors,position,geoservices,texttospeech,qmllint&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;############ E. Qt: remove unused Python bindings ############&lt;/span&gt;
&lt;span class="nv"&gt;KEEP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;" QtCore QtGui QtWidgets QtNetwork QtWebChannel QtWebEngineCore QtWebEngineWidgets QtPrintSupport QtDBus QtOpenGL QtOpenGLWidgets QtSvg "&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$SP&lt;/span&gt;/PyQt6/&lt;span class="k"&gt;*&lt;/span&gt;.abi3.so&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;m&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;m&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;m&lt;/span&gt;&lt;span class="p"&gt;%%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEEP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SP&lt;/span&gt;&lt;span class="s2"&gt;/PyQt6/&lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="s2"&gt;.pyi"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;############ F. Qt: delete C++ libs nothing depends on (dependency closure) ############&lt;/span&gt;
&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; need
&lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt; &lt;span class="nv"&gt;$SP&lt;/span&gt;/PyQt6/&lt;span class="k"&gt;*&lt;/span&gt;.abi3.so &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="s2"&gt;/libexec/QtWebEngineProcess"&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; p&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;queue+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$QT&lt;/span&gt;&lt;span class="s2"&gt;/plugins"&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'*.so'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;:1&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;n &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;patchelf &lt;span class="nt"&gt;--print-needed&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'^libQt6'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;%%.so*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;need&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;continue
    &lt;/span&gt;need[&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;1
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;$QTLIB&lt;/span&gt;/&lt;span class="nv"&gt;$n&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; queue+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$QTLIB&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;done
done

for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$QTLIB&lt;/span&gt;/libQt6&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="p"&gt;%%.so*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;need&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"removing &lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;############ G. Permissions (same as before) ############&lt;/span&gt;
find AppDir &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"QtWebEngineProcess"&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;

&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; AppDir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test before packaging. If it fails, paste me the error output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./AppDir/AppRun
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Package with better compression
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./appimagetool-x86_64.AppImage &lt;span class="nt"&gt;--comp&lt;/span&gt; zstd &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mksquashfs-opt&lt;/span&gt; &lt;span class="nt"&gt;-Xcompression-level&lt;/span&gt; &lt;span class="nt"&gt;--mksquashfs-opt&lt;/span&gt; 19 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mksquashfs-opt&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;--mksquashfs-opt&lt;/span&gt; 1M &lt;span class="se"&gt;\&lt;/span&gt;
  AppDir uniclare_client.AppImage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want it even smaller and don't mind a slightly slower startup, use &lt;code&gt;--comp xz&lt;/code&gt; and drop the zstd-specific options.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to expect
&lt;/h3&gt;

&lt;p&gt;The 651M AppDir should shrink to roughly 400-450M, and the AppImage to about 130-160 MB. Most of what remains is &lt;code&gt;libQt6WebEngineCore&lt;/code&gt; and the Chromium resources, which you can't remove while using Qt WebEngine.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'd like to see next
&lt;/h3&gt;

&lt;p&gt;To find any remaining big items specific to your app, paste the output of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; AppDir/usr/lib/unidesk/site-packages/&lt;span class="k"&gt;*&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-15&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; AppDir/usr/lib/unidesk/site-packages/PyQt6/Qt6/&lt;span class="k"&gt;*&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;tail
&lt;/span&gt;uv python find 3.12 &lt;span class="nt"&gt;--no-project&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last command confirms the copied Python is a uv-managed standalone build and not the system Python. If it prints something under &lt;code&gt;/usr&lt;/code&gt;, that alone would explain part of the 111M.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>python</category>
      <category>tools</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to create a flatpak of a python gui project</title>
      <dc:creator>viraj-sh</dc:creator>
      <pubDate>Thu, 29 Oct 2026 08:48:13 +0000</pubDate>
      <link>https://dev.to/virajsh/how-to-create-a-flatpak-of-a-python-gui-project-4fln</link>
      <guid>https://dev.to/virajsh/how-to-create-a-flatpak-of-a-python-gui-project-4fln</guid>
      <description>&lt;h3&gt;
  
  
  1. Install required tools and base flatpak packages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;flatpak flatpak-builder pipx appstream unzip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;from flatpak we are going to install latest node extension and gnome (sdk &amp;amp; platform)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak &lt;span class="nb"&gt;install &lt;/span&gt;org.freedesktop.Sdk.Extension.node26
flatpak &lt;span class="nb"&gt;install &lt;/span&gt;org.gnome.Sdk//51
flatpak &lt;span class="nb"&gt;install &lt;/span&gt;org.gnome.Platform//51
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pip and node generator&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://raw.githubusercontent.com/flatpak/flatpak-builder-tools/master/pip/flatpak-pip-generator.py

wget https://github.com/flatpak/flatpak-builder-tools/archive/refs/heads/master.zip
unzip master.zip &lt;span class="s2"&gt;"flatpak-builder-tools-master/node/*"&lt;/span&gt;
pipx &lt;span class="nb"&gt;install&lt;/span&gt; ./flatpak-builder-tools-master/node/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify python exists and version in gnome Sdk&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak run &lt;span class="nt"&gt;--command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python3 org.gnome.Sdk//51 &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Package Files
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;packaging/flatpak&lt;/code&gt; dir in the root add &lt;code&gt;.desktop&lt;/code&gt;, &lt;code&gt;.xml&lt;/code&gt;, &lt;code&gt;.svg&lt;/code&gt;, &lt;code&gt;.yml&lt;/code&gt; files&lt;/p&gt;

&lt;p&gt;&lt;code&gt;io.github.viraj_sh.uniclare_client.desktop&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Desktop Entry]
Name=Uniclare Client
Exec=unidesk
Icon=io.github.viraj_sh.uniclare_client
Terminal=false
Type=Application
Categories=Utility;
StartupWMClass=unidesk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;io.github.viraj_sh.uniclare_client.metainfo.xml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;component&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"desktop-application"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;io.github.viraj_sh.uniclare_client&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Uniclare Client&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;View data that is hidden on the Uniclare App or Student Portal&lt;span class="nt"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;metadata_license&amp;gt;&lt;/span&gt;MIT&lt;span class="nt"&gt;&amp;lt;/metadata_license&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;project_license&amp;gt;&lt;/span&gt;MIT&lt;span class="nt"&gt;&amp;lt;/project_license&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Uniclare Client lets you view data that is hidden on the Uniclare App or Student Portal.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;launchable&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"desktop-id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;io.github.viraj_sh.uniclare_client.desktop&lt;span class="nt"&gt;&amp;lt;/launchable&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;developer&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"io.github.viraj_sh"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;viraj-sh&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/developer&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;url&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"homepage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;https://github.com/viraj-sh/uniclare-client&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;content_rating&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"oars-1.1"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;releases&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;release&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"3.0.0"&lt;/span&gt; &lt;span class="na"&gt;date=&lt;/span&gt;&lt;span class="s"&gt;"2026-10-25"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/releases&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/component&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;io.github.viraj_sh.uniclare_client.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;io.github.viraj_sh.uniclare_client&lt;/span&gt;
&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;org.gnome.Platform&lt;/span&gt;
&lt;span class="na"&gt;runtime-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;51'&lt;/span&gt;
&lt;span class="na"&gt;sdk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;org.gnome.Sdk&lt;/span&gt;
&lt;span class="na"&gt;sdk-extensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;org.freedesktop.Sdk.Extension.node26&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unidesk&lt;/span&gt;

&lt;span class="na"&gt;finish-args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--share=network&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--share=ipc&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--socket=wayland&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--socket=fallback-x11&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--socket=system-bus&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--device=dri&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--env=PYWEBVIEW_GUI=gtk&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--env=SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt&lt;/span&gt;

&lt;span class="na"&gt;modules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python3-deps.json&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python3-uv-build.json&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uniclare-client&lt;/span&gt;
    &lt;span class="na"&gt;buildsystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;simple&lt;/span&gt;
    &lt;span class="na"&gt;build-options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;append-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/usr/lib/sdk/node26/bin&lt;/span&gt;
      &lt;span class="na"&gt;strip&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;build-commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd web &amp;amp;&amp;amp; npm ci --offline --cache=$FLATPAK_BUILDER_BUILDDIR/flatpak-node/npm-cache &amp;amp;&amp;amp; npm run build&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip3 install --prefix=/app --no-deps --no-build-isolation .&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;install -Dm644 packaging/flatpak/io.github.viraj_sh.uniclare_client.desktop /app/share/applications/io.github.viraj_sh.uniclare_client.desktop&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;install -Dm644 packaging/flatpak/io.github.viraj_sh.uniclare_client.metainfo.xml /app/share/metainfo/io.github.viraj_sh.uniclare_client.metainfo.xml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;install -Dm644 packaging/flatpak/io.github.viraj_sh.uniclare_client.svg /app/share/icons/hicolor/scalable/apps/io.github.viraj_sh.uniclare_client.svg&lt;/span&gt;
    &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dir&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../..&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;generated-sources.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validate .xml file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;appstreamcli validate &lt;span class="nt"&gt;--pedantic&lt;/span&gt; packaging/flatpak/io.github.viraj_sh.uniclare_client.metainfo.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Dependences
&lt;/h3&gt;

&lt;p&gt;Generate &lt;code&gt;requirements.txt&lt;/code&gt; and remove pyqt or gnome related libraries since we are using flatpak gnome base package for the same&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv pip compile pyproject.toml &lt;span class="nt"&gt;--extra&lt;/span&gt; desktop &lt;span class="nt"&gt;-o&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-viE&lt;/span&gt; &lt;span class="s1"&gt;'^(pyqt6|qtpy|pygobject|pycairo)'&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.clean.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;requirements.clean.txt requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this to generate &lt;code&gt;python3-deps.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SSL_CERT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; certifi&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# optional nixos&lt;/span&gt;

python3 flatpak-pip-generator.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'org.gnome.Sdk//51'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prefer-wheels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pydantic-core &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--requirements-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;requirements.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; ./packaging/flatpak/python3-deps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this to generate &lt;code&gt;python3-uv-build.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv pip &lt;span class="nb"&gt;install &lt;/span&gt;pip
python &lt;span class="nt"&gt;-m&lt;/span&gt; pip download &lt;span class="s2"&gt;"uv_build&amp;gt;=0.11.21,&amp;lt;0.12.0"&lt;/span&gt; &lt;span class="nt"&gt;--only-binary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;:all: &lt;span class="nt"&gt;--no-deps&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; /tmp/uvb
&lt;span class="nb"&gt;ls&lt;/span&gt; /tmp/uvb   &lt;span class="c"&gt;# confirm a .whl is present&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"uv_build==0.11.33"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; uvb.txt
python3 flatpak-pip-generator.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'org.gnome.Sdk//51'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prefer-wheels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;uv_build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--requirements-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;uvb.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; ./packaging/flatpak/python3-uv-build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this to generate &lt;code&gt;generated-sources.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak-node-generator npm web/package-lock.json &lt;span class="nt"&gt;-o&lt;/span&gt; generated-sources.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Build &amp;amp; Test Locally
&lt;/h3&gt;

&lt;p&gt;To build run this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak-builder &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--force-clean&lt;/span&gt; &lt;span class="nt"&gt;--install-deps-from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;flathub &lt;span class="nt"&gt;--disable-rofiles-fuse&lt;/span&gt; build-dir ./packaging/flatpak/io.github.viraj_sh.uniclare_client.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To install run this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak-builder &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--force-clean&lt;/span&gt; &lt;span class="nt"&gt;--install-deps-from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;flathub &lt;span class="nt"&gt;--install&lt;/span&gt; &lt;span class="nt"&gt;--repo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;repo build-dir ./packaging/flatpak/io.github.viraj_sh.uniclare_client.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To RUN the app run this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak run io.github.viraj_sh.uniclare_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Create a Bundle
&lt;/h3&gt;

&lt;p&gt;To create a bundle run this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak build-bundle ./packaging/flatpak/repo uniclare-client.flatpak io.github.viraj_sh.uniclare_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the bundle size&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; ./packaging/flatpak/uniclare-client.flatpak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Clean UP
&lt;/h3&gt;

&lt;p&gt;To uninstall the app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak uninstall &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; io.github.viraj_sh.uniclare_client &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To cleanup flatpak packages&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak uninstall &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--unused&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove build dir and cache&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ./packaging/flatpak/build-dir ./packaging/flatpak/repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove apps persisted data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/.var/app/io.github.viraj_sh.uniclare_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove flatpak builder cache&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; .flatpak-builder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>linux</category>
      <category>programming</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>this solved my error</title>
      <dc:creator>viraj-sh</dc:creator>
      <pubDate>Thu, 29 Oct 2026 06:28:48 +0000</pubDate>
      <link>https://dev.to/virajsh/this-solved-my-error-3na9</link>
      <guid>https://dev.to/virajsh/this-solved-my-error-3na9</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isDesktop&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;viaBridge&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bridgeReady&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isDesktop&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;viaBridge&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;viaHttp&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
    </item>
    <item>
      <title>how to create flatpak package of python project</title>
      <dc:creator>viraj-sh</dc:creator>
      <pubDate>Sun, 25 Oct 2026 11:16:23 +0000</pubDate>
      <link>https://dev.to/virajsh/how-to-create-flatpak-package-of-python-project-104a</link>
      <guid>https://dev.to/virajsh/how-to-create-flatpak-package-of-python-project-104a</guid>
      <description>&lt;ol&gt;
&lt;li&gt;Install required tools
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;flatpak flatpak-builder pipx appstream
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak &lt;span class="nb"&gt;install &lt;/span&gt;flathub org.kde.Platform//6.11 org.kde.Sdk//6.11 com.riverbankcomputing.PyQt.BaseApp//6.11 org.freedesktop.Sdk.Extension.node24//25.08
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;download pip generator&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://raw.githubusercontent.com/flatpak/flatpak-builder-tools/master/pip/flatpak-pip-generator.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;download and install node generator&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://github.com/flatpak/flatpak-builder-tools/archive/refs/heads/master.zip
unzip master.zip &lt;span class="s2"&gt;"flatpak-builder-tools-master/node/*"&lt;/span&gt;
pipx &lt;span class="nb"&gt;install&lt;/span&gt; ./flatpak-builder-tools-master/node/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Verify Python and QWebEngineView
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak run &lt;span class="nt"&gt;--command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python3 org.kde.Sdk//6.11 &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak run &lt;span class="nt"&gt;--command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python3 com.riverbankcomputing.PyQt.BaseApp//6.11 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"from PyQt6.QtWebEngineWidgets import QWebEngineView; print('PyQt6 WebEngine OK')"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Packaging files&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;make &lt;code&gt;packaging/flatpak dir&lt;/code&gt; in the root add the following files &lt;/p&gt;

&lt;p&gt;&lt;code&gt;io.github.user_name.project_name.desktop&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Desktop Entry]
Name=Uniclare Client
Exec=unidesk
Icon=io.github.viraj_sh.uniclare_client
Terminal=false
Type=Application
Categories=Utility;
StartupWMClass=unidesk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;io.github.user_name.project_name.metainfo.xml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;component&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"desktop-application"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;io.github.viraj_sh.uniclare_client&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Uniclare Client&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;View data that is hidden on the Uniclare App or Student Portal&lt;span class="nt"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;metadata_license&amp;gt;&lt;/span&gt;MIT&lt;span class="nt"&gt;&amp;lt;/metadata_license&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;project_license&amp;gt;&lt;/span&gt;MIT&lt;span class="nt"&gt;&amp;lt;/project_license&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Uniclare Client lets you view data that is hidden on the Uniclare App or Student Portal.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;launchable&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"desktop-id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;io.github.viraj_sh.uniclare_client.desktop&lt;span class="nt"&gt;&amp;lt;/launchable&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;developer&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"io.github.viraj_sh"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;viraj-sh&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/developer&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;url&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"homepage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;https://github.com/viraj-sh/uniclare-client&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;content_rating&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"oars-1.1"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;releases&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;release&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"3.0.0"&lt;/span&gt; &lt;span class="na"&gt;date=&lt;/span&gt;&lt;span class="s"&gt;"2026-10-25"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/releases&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/component&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;io.github.user_name.project_name.svg&lt;/code&gt; or 512px png&lt;/p&gt;

&lt;p&gt;then to verify the metinfo.xml run this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;appstream
appstreamcli validate &lt;span class="nt"&gt;--pedantic&lt;/span&gt; packaging/flatpak/io.github.viraj_sh.UniclareClient.metainfo.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Dependencies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;now generate a requirements.txt and remove pyqt related packages do not remove qtpy its still needed remove pyqt related stuff&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv pip compile pyproject.toml &lt;span class="nt"&gt;--extra&lt;/span&gt; desktop &lt;span class="nt"&gt;-o&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-viE&lt;/span&gt; &lt;span class="s1"&gt;'^(pyqt6|pyqt6-qt6|pyqt6-sip|pyqt6-webengine|pyqt6-webengine-qt6)'&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.clean.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;requirements.clean.txt requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then run this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SSL_CERT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; certifi&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# optional nixos&lt;/span&gt;

python3 flatpak-pip-generator.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'org.kde.Sdk//6.11'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prefer-wheels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pydantic-core &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--requirements-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;requirements.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; ./packaging/flatpak/python3-deps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;move this &lt;code&gt;python3-deps.json&lt;/code&gt; into &lt;code&gt;/packaging/flatpak/*&lt;/code&gt; dir&lt;/p&gt;

&lt;p&gt;then generate the uv_build module (separate, because uv_build is a Rust/maturin sdist by default and must resolve to a manylinux wheel):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv pip &lt;span class="nb"&gt;install &lt;/span&gt;pip
python &lt;span class="nt"&gt;-m&lt;/span&gt; pip download &lt;span class="s2"&gt;"uv_build&amp;gt;=0.11.21,&amp;lt;0.12.0"&lt;/span&gt; &lt;span class="nt"&gt;--only-binary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;:all: &lt;span class="nt"&gt;--no-deps&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; /tmp/uvb
&lt;span class="nb"&gt;ls&lt;/span&gt; /tmp/uvb   &lt;span class="c"&gt;# confirm a .whl is present&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"uv_build==0.11.33"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; uvb.txt
python3 flatpak-pip-generator.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'org.kde.Sdk//6.11'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prefer-wheels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;uv_build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--requirements-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;uvb.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; ./packaging/flatpak/python3-uv-build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;open &lt;code&gt;python3-uv-build.json&lt;/code&gt; and confirm the source URL ends with &lt;code&gt;.whl&lt;/code&gt; (not &lt;code&gt;.tar.gz&lt;/code&gt;). move &lt;code&gt;python3-uv-build.json&lt;/code&gt; into &lt;code&gt;/packaging/flatpak/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;then run this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak-node-generator npm web/package-lock.json &lt;span class="nt"&gt;-o&lt;/span&gt; generated-sources.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;move this &lt;code&gt;generated-sources.json&lt;/code&gt; into &lt;code&gt;/packaging/flatpak/*/&lt;/code&gt; dir&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;now make a .yml file in /package/flatpak
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;io.github.viraj_sh.uniclare_client&lt;/span&gt;
&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;org.kde.Platform&lt;/span&gt;
&lt;span class="na"&gt;runtime-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;6.11'&lt;/span&gt;
&lt;span class="na"&gt;sdk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;org.kde.Sdk&lt;/span&gt;
&lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.riverbankcomputing.PyQt.BaseApp&lt;/span&gt;
&lt;span class="na"&gt;base-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;6.11'&lt;/span&gt;
&lt;span class="na"&gt;sdk-extensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;org.freedesktop.Sdk.Extension.node24&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unidesk&lt;/span&gt;

&lt;span class="na"&gt;cleanup-commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/app/cleanup-BaseApp.sh&lt;/span&gt;

&lt;span class="na"&gt;finish-args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--share=network&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--share=ipc&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--socket=wayland&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--socket=fallback-x11&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--socket=system-bus&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--device=dri&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--env=PYWEBVIEW_GUI=qt&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--env=QTWEBENGINEPROCESS_PATH=/app/bin/QtWebEngineProcess&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--env=SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt&lt;/span&gt;
&lt;span class="na"&gt;modules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python3-deps.json&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python3-uv-build.json&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uniclare-client&lt;/span&gt;
    &lt;span class="na"&gt;buildsystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;simple&lt;/span&gt;
    &lt;span class="na"&gt;build-options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;append-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/usr/lib/sdk/node24/bin&lt;/span&gt;
      &lt;span class="na"&gt;strip&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;build-commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd web &amp;amp;&amp;amp; npm ci --offline --cache=$FLATPAK_BUILDER_BUILDDIR/flatpak-node/npm-cache &amp;amp;&amp;amp; npm run build&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip3 install --prefix=/app --no-deps --no-build-isolation .&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;install -Dm644 packaging/flatpak/io.github.viraj_sh.uniclare_client.desktop /app/share/applications/io.github.viraj_sh.uniclare_client.desktop&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;install -Dm644 packaging/flatpak/io.github.viraj_sh.uniclare_client.metainfo.xml /app/share/metainfo/io.github.viraj_sh.uniclare_client.metainfo.xml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;install -Dm644 packaging/flatpak/io.github.viraj_sh.uniclare_client.svg /app/share/icons/hicolor/scalable/apps/io.github.viraj_sh.uniclare_client.svg&lt;/span&gt;
    &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dir&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../..&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;generated-sources.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then run this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak-builder &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--force-clean&lt;/span&gt; &lt;span class="nt"&gt;--install-deps-from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;flathub build-dir io.github.viraj_sh.uniclare_client.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Build &amp;amp; Test&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;install app locally&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak-builder &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--force-clean&lt;/span&gt; &lt;span class="nt"&gt;--install-deps-from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;flathub &lt;span class="nt"&gt;--install&lt;/span&gt; &lt;span class="nt"&gt;--repo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;repo build-dir io.github.viraj_sh.uniclare_client.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak run io.github.viraj_sh.uniclare_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Clean everything for updating or testing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Remove the flatpak-builder state/cache directory, build directory and local repo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; .flatpak-builder
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; build-dir repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Remove the app's persisted data (localStorage, token, webview cache)
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/.var/app/io.github.viraj_sh.uniclare_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Uninstall the currently installed flatpak app
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak uninstall &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; io.github.viraj_sh.uniclare_client &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;flatpak uninstall &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--unused&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Rebuild and test&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Build&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak-builder &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--force-clean&lt;/span&gt; &lt;span class="nt"&gt;--install-deps-from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;flathub &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;repo build-dir io.github.viraj_sh.uniclare_client.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ./repo io.github.viraj_sh.uniclare_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak run io.github.viraj_sh.uniclare_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a bundle&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create the new bundle&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak build-bundle repo uniclare-client.flatpak io.github.viraj_sh.uniclare_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check bundle size&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; uniclare-client.flatpak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>cli</category>
      <category>linux</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Running pywebview on NixOS with a single shell.nix</title>
      <dc:creator>viraj-sh</dc:creator>
      <pubDate>Sun, 25 Oct 2026 08:04:52 +0000</pubDate>
      <link>https://dev.to/virajsh/running-pywebview-on-nixos-with-a-single-shellnix-2dmg</link>
      <guid>https://dev.to/virajsh/running-pywebview-on-nixos-with-a-single-shellnix-2dmg</guid>
      <description>&lt;p&gt;How to run pywebview (Qt backend) on NixOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;pywebview's Qt backend uses prebuilt wheels (PyQt6 + QtWebEngine). On NixOS they can't find the shared libraries they expect, so the libraries have to be exposed through &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;shell.nix&lt;/code&gt; below includes everything QtWebEngine/Chromium needs, so it works on a fresh NixOS install with nothing else installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  shell.nix
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt;
  &lt;span class="nv"&gt;runtimeLibs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kn"&gt;with&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c"&gt;# Base&lt;/span&gt;
    &lt;span class="nv"&gt;stdenv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;cc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;cc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lib&lt;/span&gt;
    &lt;span class="nv"&gt;zlib&lt;/span&gt;
    &lt;span class="nv"&gt;zstd&lt;/span&gt;
    &lt;span class="nv"&gt;brotli&lt;/span&gt;

    &lt;span class="c"&gt;# GL / graphics&lt;/span&gt;
    &lt;span class="nv"&gt;libGL&lt;/span&gt;
    &lt;span class="nv"&gt;libglvnd&lt;/span&gt; &lt;span class="c"&gt;# libEGL&lt;/span&gt;
    &lt;span class="nv"&gt;libgbm&lt;/span&gt;
    &lt;span class="nv"&gt;libdrm&lt;/span&gt;
    &lt;span class="nv"&gt;vulkan-loader&lt;/span&gt;

    &lt;span class="c"&gt;# Wayland / input&lt;/span&gt;
    &lt;span class="nv"&gt;wayland&lt;/span&gt;
    &lt;span class="nv"&gt;libxkbcommon&lt;/span&gt;

    &lt;span class="c"&gt;# X11&lt;/span&gt;
    &lt;span class="nv"&gt;libx11&lt;/span&gt;
    &lt;span class="nv"&gt;libxext&lt;/span&gt;
    &lt;span class="nv"&gt;libxrandr&lt;/span&gt;
    &lt;span class="nv"&gt;libxcomposite&lt;/span&gt;
    &lt;span class="nv"&gt;libxdamage&lt;/span&gt;
    &lt;span class="nv"&gt;libxfixes&lt;/span&gt;
    &lt;span class="nv"&gt;libxrender&lt;/span&gt;
    &lt;span class="nv"&gt;libxi&lt;/span&gt;
    &lt;span class="nv"&gt;libxcursor&lt;/span&gt;
    &lt;span class="nv"&gt;libxtst&lt;/span&gt;
    &lt;span class="nv"&gt;libxkbfile&lt;/span&gt;
    &lt;span class="nv"&gt;libxshmfence&lt;/span&gt;

    &lt;span class="c"&gt;# xcb&lt;/span&gt;
    &lt;span class="nv"&gt;libxcb&lt;/span&gt;
    &lt;span class="nv"&gt;libxcb-util&lt;/span&gt;
    &lt;span class="nv"&gt;libxcb-cursor&lt;/span&gt;
    &lt;span class="nv"&gt;libxcb-wm&lt;/span&gt;
    &lt;span class="nv"&gt;libxcb-image&lt;/span&gt;
    &lt;span class="nv"&gt;libxcb-keysyms&lt;/span&gt;
    &lt;span class="nv"&gt;libxcb-render-util&lt;/span&gt;

    &lt;span class="c"&gt;# GTK stack / fonts&lt;/span&gt;
    &lt;span class="nv"&gt;glib&lt;/span&gt;
    &lt;span class="nv"&gt;gtk3&lt;/span&gt;
    &lt;span class="nv"&gt;gdk-pixbuf&lt;/span&gt;
    &lt;span class="nv"&gt;pango&lt;/span&gt;
    &lt;span class="nv"&gt;cairo&lt;/span&gt;
    &lt;span class="nv"&gt;atk&lt;/span&gt;
    &lt;span class="nv"&gt;at-spi2-atk&lt;/span&gt;
    &lt;span class="nv"&gt;harfbuzz&lt;/span&gt;
    &lt;span class="nv"&gt;fontconfig&lt;/span&gt;
    &lt;span class="nv"&gt;freetype&lt;/span&gt;

    &lt;span class="c"&gt;# QtWebEngine / Chromium&lt;/span&gt;
    &lt;span class="nv"&gt;dbus&lt;/span&gt;
    &lt;span class="nv"&gt;expat&lt;/span&gt;
    &lt;span class="nv"&gt;nss&lt;/span&gt;
    &lt;span class="nv"&gt;nspr&lt;/span&gt;
    &lt;span class="nv"&gt;cups&lt;/span&gt;
    &lt;span class="nv"&gt;alsa-lib&lt;/span&gt;
    &lt;span class="nv"&gt;libpulseaudio&lt;/span&gt;
    &lt;span class="nv"&gt;krb5&lt;/span&gt;
    &lt;span class="nv"&gt;icu&lt;/span&gt;
    &lt;span class="nv"&gt;libpng&lt;/span&gt;
    &lt;span class="nv"&gt;libjpeg&lt;/span&gt;
    &lt;span class="nv"&gt;libwebp&lt;/span&gt;
    &lt;span class="nv"&gt;libxml2&lt;/span&gt;
    &lt;span class="nv"&gt;libxslt&lt;/span&gt;
    &lt;span class="nv"&gt;lcms2&lt;/span&gt;
    &lt;span class="nv"&gt;libopus&lt;/span&gt;
    &lt;span class="nv"&gt;libevent&lt;/span&gt;
    &lt;span class="nv"&gt;minizip&lt;/span&gt;
    &lt;span class="nv"&gt;snappy&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kn"&gt;in&lt;/span&gt;
&lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mkShell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;uv&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nv"&gt;LD_LIBRARY_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;makeLibraryPath&lt;/span&gt; &lt;span class="nv"&gt;runtimeLibs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;# --pure sets this to a nonexistent file, which breaks httpx&lt;/span&gt;
  &lt;span class="nv"&gt;SSL_CERT_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;cacert&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/etc/ssl/certs/ca-bundle.crt"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;QTWEBENGINE_DISABLE_SANDBOX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;PYWEBVIEW_GUI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"qt"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What the non-obvious bits do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt;: makes the libs above visible to the pip wheels&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SSL_CERT_FILE&lt;/code&gt;: only needed with &lt;code&gt;nix-shell --pure&lt;/code&gt;, otherwise httpx breaks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;QTWEBENGINE_DISABLE_SANDBOX&lt;/code&gt;: the Chromium sandbox doesn't work here, so it's disabled&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PYWEBVIEW_GUI=qt&lt;/code&gt;: forces the Qt backend&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nix-shell
uv init
uv add &lt;span class="s2"&gt;"pywebview[qt]"&lt;/span&gt;
uv run main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  main.py
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webview&lt;/span&gt;

&lt;span class="n"&gt;webview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;webview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>nixos</category>
      <category>python</category>
      <category>pywebview</category>
      <category>linux</category>
    </item>
    <item>
      <title>Develop and Publish Python Packages to PyPI</title>
      <dc:creator>viraj-sh</dc:creator>
      <pubDate>Sat, 24 Oct 2026 22:20:49 +0000</pubDate>
      <link>https://dev.to/virajsh/develop-and-publish-python-packages-to-pypi-2mbn</link>
      <guid>https://dev.to/virajsh/develop-and-publish-python-packages-to-pypi-2mbn</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pick a package name that isn't already taken on &lt;a href="https://pypi.org/project/" rel="noopener noreferrer"&gt;https://pypi.org/project/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Register an account on &lt;a href="https://pypi.org/" rel="noopener noreferrer"&gt;https://pypi.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Install &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt; and &lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt; on your system&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Initialize the project
&lt;/h3&gt;

&lt;p&gt;Run this in the directory where you want the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv init &lt;span class="nt"&gt;--package&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; project_name &lt;span class="nt"&gt;--build-backend&lt;/span&gt; uv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a &lt;code&gt;src/project_name&lt;/code&gt; package and a &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Write your code
&lt;/h3&gt;

&lt;p&gt;Start coding inside &lt;code&gt;./src/project_name&lt;/code&gt; — that's where the package source lives. All your modules go here.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Update the project metadata
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;pyproject.toml&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update description&lt;/li&gt;
&lt;li&gt;Add a license&lt;/li&gt;
&lt;li&gt;Update the author name and email&lt;/li&gt;
&lt;li&gt;Add your dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also update &lt;code&gt;README.md&lt;/code&gt; and &lt;code&gt;LICENSE&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. (Optional) Add entry points for a CLI app or MCP server
&lt;/h3&gt;

&lt;p&gt;If your app is a CLI or an MCP server, you can expose a command that gets installed on the user's system. Add an &lt;code&gt;entry.py&lt;/code&gt; under the relevant subpackage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLI entry point&lt;/strong&gt; — &lt;code&gt;src/package_name/cli/entry.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/package_name/cli/entry.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;package_name.cli.main&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cli&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ModuleNotFoundError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;typer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keyring&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing CLI deps. Install: uv tool install &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;package_name[cli]&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
    &lt;span class="nf"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MCP server entry point&lt;/strong&gt; — &lt;code&gt;src/package_name/mcp_server/entry.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/package_name/mcp_server/entry.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;package_name.mcp_server.server&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ModuleNotFoundError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fastmcp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing MCP deps. Install: uv tool install &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;package_name[mcp]&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then register the commands in &lt;code&gt;pyproject.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="k"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;command_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"package_name.cli.entry:run"&lt;/span&gt;
&lt;span class="n"&gt;mcp_server_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"package_name.mcp_server.entry:run"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; the &lt;code&gt;[cli]&lt;/code&gt; and &lt;code&gt;[mcp]&lt;/code&gt; extras used above must be defined under &lt;code&gt;[project.optional-dependencies]&lt;/code&gt; in &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5. Update .gitignore and test locally
&lt;/h3&gt;

&lt;p&gt;Add any files that should not be committed (virtual environments, &lt;code&gt;dist/&lt;/code&gt;, secrets, etc.) to &lt;code&gt;.gitignore&lt;/code&gt;, then test the application locally.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Build the package
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a &lt;code&gt;.tar.gz&lt;/code&gt; (source distribution) and a &lt;code&gt;.whl&lt;/code&gt; (wheel) in &lt;code&gt;./dist&lt;/code&gt;. Extract the &lt;code&gt;.tar.gz&lt;/code&gt; and check its contents to make sure no unnecessary files are included.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Test the build locally
&lt;/h3&gt;

&lt;p&gt;Before publishing, install the wheel to confirm it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As a library&lt;/strong&gt; — install it into a virtual environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv pip &lt;span class="nb"&gt;install&lt;/span&gt; ./dist/package_name-x.x.x-py3-none-any.whl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;As a CLI or MCP server&lt;/strong&gt; — &lt;code&gt;uv tool install&lt;/code&gt; is meant for packages that expose a command (a CLI entry point or an MCP server entry point). Install the wheel with the relevant extra:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"package_name[cli] @ ./dist/package_name-x.x.x-py3-none-any.whl"&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
uv tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"package_name[mcp] @ ./dist/package_name-x.x.x-py3-none-any.whl"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8. Publish to PyPI
&lt;/h3&gt;

&lt;p&gt;Log in at &lt;a href="https://pypi.org/manage/account/" rel="noopener noreferrer"&gt;https://pypi.org/manage/account/&lt;/a&gt; and create an API token.&lt;/p&gt;

&lt;p&gt;Use the token to publish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;UV_PUBLISH_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pypi-...
uv publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package is now live under your account, and anyone can install it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;package_name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; a new token can be scoped to your whole account only until the project exists. After the first upload, create a token scoped to that project and use it for future releases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  9. Publish updates
&lt;/h3&gt;

&lt;p&gt;After making code changes:&lt;/p&gt;

&lt;p&gt;Bump the version in &lt;code&gt;pyproject.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv version &lt;span class="nt"&gt;--bump&lt;/span&gt; patch   &lt;span class="c"&gt;# or minor / major&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean old builds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; dist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Publish (with &lt;code&gt;UV_PUBLISH_TOKEN&lt;/code&gt; set):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also pass the token directly with &lt;code&gt;uv publish --token pypi-...&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; PyPI does not allow re-uploading a version that already exists, so always bump the version before publishing.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>python</category>
      <category>pypi</category>
    </item>
  </channel>
</rss>
