Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

Hosting a Flatpak repo

In the last post, I explained how I built my first flatpak! So now that I have a flatpak I want to distribute to my users, how do I do that? A web-based repository, like everything else!

Preparing the repo

A flatpak repo is just a web directory. For one of these hipster modern techs, they made a good decision for a web repo!

I built a custom internal.flatpakrepo file, and for convenience I placed in in that same directory, but it doesn't have to be there.

[Flatpak Repo]
Title=Internal Flatpak Repo
Url=http://server3/internal/repo/flatpak/
Homepage=http://server3/internal/repo/flatpak/
Comment=Local flatpak repo
Description=All my local flatpaks go here
Icon=http://server3/internal/repo/flatpak/icon.png

At least Gnome Software does not seem to use the repo icon, so I guess my chasing one down was pointless, but whatever. I think it works better with a 128x128 icon for the repo, and letting Gnome Software cache it a few times before you expect it to see it (?), but sometimes it works.

Adding packages to the repo

It's as easy as having the destination www path, in my case /mnt/public/www/internal/repo/flatpak mounted on the system that builds the package, and then adding to the builder command --repo=/mnt/public/www/internal/repo/flatpak.

flatpak-builder build net.ddns.bgstack15.srbsavegameeditor.yml --force-clean --repo=/mnt/public/www/internal/repo/flatpak --verbose

I should have bothered to set up a gpg key, and add --gpg-sign=${GPG_KEY_ID} but my needs are modest and I didn't want to add that yet. That nfs host has a gpg key for signing my rpm repo already; I wonder if I can sign separately from building the packages.

After any new packages/releases/re-releases, I needed to update the remote repo with the latest info.

flatpak build-update-repo --prune /mnt/public/www/internal/repo/flatpak

The --prune removes unreferenced objects, which is different from --prune-depth (defaults to -1 which means infinite). The prune depth would be keep only this number of old versions. So prune shouldn't remove old versions, but I didn't have old versions with which to experiment.

Using the web repo

To use this new web repository, run a simple command.

flatpak remote-add --if-not-exists --user internal http://server3/internal/repo/flatpak/internal.flatpakrepo

To update the available app information from that repo, run this.

flatpak update internal --appstream

To search for applications, use command flatpak search. Ironically, the --columns does not take all:f, so if you want to force full (no ellipses) view of each column, you have to spell everything out out.

$ flatpak search srbsave --columns 'name:f,description:f,application:f,version:f,branch:f,remote:f'
Name                Description                                      Application ID                       Version Branch Remotes
SRB Savegame Editor Edit savegame files for Snoopy vs. The Red Baron net.ddns.bgstack15.srbsavegameeditor 0.0.1   master internal

A web frontend

I wrote a small front page generator script that makes a nice web landing page.

files/2025/listings/generate-flatpakrepo-webpage.sh (Source)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
#!/bin/sh
# File: /mnt/public/Support/Programs/flatpak-repo/generate-flatpakrepo-webpage.sh 
# Location: server3
# Author: bgstack15
# Startdate: 2025-04-04-6 12:02
# SPDX-License-Identifier: GPL-3.0-only
# Title: Generate Webpage for Flatpak Repo
# Purpose: generate index.html for my flatpak repo
# History:
# Usage:
#    REPODIR="/mnt/public/www/internal/repo/flatpak/" sh ./generate-flatpakrepo-webpage.sh 
# References:
#    https://gist.github.com/intika/8e4a7faeb72c3a393e42ac9af85b62b7
# Improve:
# Dependencies:
#    dep-devuan: flatpak, ostree
# Documentation:
#    /mnt/public/www/Support/Programs/flatpak-repo/flatpak-repo.md
#    the repo icon should be 128x128 for gnome-software to read it
ferror() {
   printf '%s\n' "${@}" 1>&2
}

REPODIR="${REPODIR:-${1}}" ; test -z "${REPODIR}" && { ferror "Fatal! Please set REPODIR. Aborted." ; exit 1 ; }
# only grab first repo file
_repofile="$( find "${REPODIR}" -maxdepth 1 -mindepth 1 -iname '*.flatpakrepo' -print -quit | head -n1 )"
_reponame="$( basename "${_repofile%%.flatpakrepo}" )"
test -z "${_reponame}" && { ferror "Fatal! Unable to find a *.flatpakrepo file in ${REPODIR}. Aborted." ; }

# If you do not cd, then just add --repo="${REPODIR}"

# If using a repo by name, then you have to add it and update it.
# must have up-to-date info on this client side
flatpak remote-add --user --if-not-exists "${_reponame}" "${_repofile}"
flatpak update --appstream "${_reponame}"
# this env var makes it machine-readable output with tabs
apps="$( FLATPAK_FANCY_OUTPUT=0 flatpak --app remote-ls "${_reponame}" --columns 'name:full,application:full,description:full,version:full,installed-size:full,download-size:full,branch:full,arch:full' )"

# just view the remote-ls by file://
# but this does not show version number!
#flatpak --app remote-ls file:///${REPODIR}

count="$( echo "${apps}" | wc -l )"
cd "${REPODIR}"
index_file="${REPODIR}/index.html"
_fullreponame="$( awk -F'=' '$1~/Title/{print $2}' "${_repofile}" )"
_repowww="$( awk -F'=' '$1~/Url/{print $2}' "${_repofile}" )"
# strip trailing slash; every usage will add it to make sure it is there.
_repowww="${_repowww%%/}"
{
   echo "<html>"
   echo "<head>"
   echo '<meta name="viewport" content="width=device-width, initial-scale=1">'
   echo '<link rel="stylesheet" href="flatpak.css">'
   echo "<title>${_fullreponame}</title>"
   echo "</head>"
   echo "<body>"
   echo "<h1><img src=\"icon.png\" class='headimg'>${_fullreponame}</h1>"
   echo "<a href=\"${_reponame}.flatpakrepo\">Install this repo</a>"
   echo "<pre>flatpak remote-add --if-not-exists --user \"${_reponame}\" \"${_repowww}/${_reponame}.flatpakrepo\"</pre>"
   x=0
   _tmpfile1="$( mktemp )"
   echo "${apps}" | while IFS="$( printf '\t' )" read -r name app description version installsize downloadsize branch arch ;
   do
      x=$(( x + 1 ))
      test $x -eq 1 && {
         printf '%s\n' "<h2>Packages (${count})</h2>"
         printf '%s\n%s\n' "<table>" "<tr><th>Name</th><th>Application ID</th></th><th>Description</th><th>Version</th><th>Installed size</th><th>Download size</th></tr>"
      }
      _reffile="${REPODIR}/files/${app}.flatpakref"
      _iconfile="${REPODIR}/cache/${app}.png"
      # get icon extracted:
      # ref="$( ostree refs --list | grep -vE '\.Debug|^appstream[0-9]*' )" # app/com.example.AppName/x86_64/master
      _ref="app/${app}/${arch}/${branch}"
      _reficonfile="$( ostree ls --repo="${REPODIR}" --nul-filenames-only -R "${_ref}" 2>/dev/null | tr '\0' '\n' | grep --null -iE '\/icons\/.*64.*(png|svg)$' | head -n1 )"
      if test -n "${_reficonfile}" ;
      then
         ostree cat --repo="${REPODIR}" "${_ref}" "${_reficonfile}" > "${_iconfile}"
      fi
      # do nothing to _iconfile if we do not have material to generate it. A 404, or any previous image left behind, will suffice.
      printf '%s' "<td><a href=\"files/${app}.flatpakref\">"
      test -n "${_reficonfile}" && { printf '%s' "<img src=\"cache/${app}.png\">" ; }
      printf '%s\n' "${name}</a></td><td>${app}</td><td>${description}</td><td>${version}</td><td>${installsize}</td><td>${downloadsize}</td>"
      {
         echo "[Flatpak Ref]"
         echo "Title=${name}"
         echo "Name=${app}"
         echo "Branch=${branch}"
         echo "Url=${_repowww}/"
         echo "RuntimeRepo=${_repowww}/${_reponame}.flatpakrepo"
         echo "IsRuntime=false" # case sensitive, must be lowercase "false"
      } > "${_reffile}"
      echo "${x}" > "${_tmpfile1}"
   done
   x="$( cat "${_tmpfile1}" )"
   rm -f "${_tmpfile1}"
   test $x -gt 0 && {
      printf '%s\n' "</table>"
   }
   echo "</body>"
   echo "<footer>"
   _now="$( TZ=UTC date "+%FT%TZ" )"
   echo "Last generated: ${_now}<br/>"
   echo "</footer>"
   echo "</html>"
} > "${index_file}"
ferror "Generated at ${_now} with ${x} packages."

It extracts each app icon, and also generates the flatpakref for each app. So if your software center plays nicely with .flatpakref files, you can just click the link and open the app install screen in your software center!

References

  1. Hosting a repository - Flatpak documentation
  2. Maintaining a flatpak repository – Alexander Larsson
  3. Distributing Applications — Flatpak 0.8.0 documentation

Comments