An open API service indexing awesome lists of open source software.

https://github.com/aetherpak/actions


https://github.com/aetherpak/actions

flatpak flatpak-applications flatpak-builder flatpak-repository

Last synced: 27 days ago
JSON representation

Awesome Lists containing this project

README

          

# AetherPak Actions

GitHub Actions that build Flatpak applications and host them as a Flatpak
repository, using GitHub Container Registry (GHCR) for the package blobs and
GitHub Pages for a small registry index and a landing page.



AetherPak registry landing page with one-click Flatpak install



The page AetherPak deploys. Single-app: live demo · repo. Multi-app: live demo · repo

## Why OCI + Pages

A static-HTTP OSTree repository serves an app as many small objects, so clients
make hundreds of requests. That is slow, and it trips GitHub Pages rate limits.
AetherPak uses Flatpak's native OCI support instead:

- Application layers (blobs) live in GHCR as OCI images.
- A small JSON index (`index/static`) and a landing page are served from Pages.
- Clients read the index from Pages and pull layers from GHCR in large chunks.

## Quick start

1. Enable Pages: repository Settings, then Pages, then Source: **GitHub Actions**.
2. Add `.github/workflows/publish.yml`:

```yaml
name: Publish Flatpak
on: { push: { branches: [main] } }
permissions:
contents: read
packages: write
pages: write
id-token: write
jobs:
publish:
uses: aetherpak/actions/.github/workflows/publish.yml@v3
with:
manifest-path: org.example.App.json
```

By default the app is built for `x86_64` and `aarch64` and deployed to Pages.

After the first run, make the GHCR package public so users can install without
authenticating: the package's page, then **Package settings**, then **Change
visibility**, then **Public**.

For an organization repository, an owner must first allow public packages,
otherwise the image is created private and can't be switched. Do this **before**
the first publish: **Organization → Settings → Packages → Package creation**,
then enable **Public**.

### Options

| Input | Default | Purpose |
|---|---|---|
| `manifest-path` | _(required)_ | Flatpak manifest to build |
| `arches` | `x86_64 aarch64` | architectures to build |
| `branch` | `stable` on tags, `beta` on the default branch, else the ref name | Flatpak branch (channel) |
| `deploy` | `true` | deploy to Pages; `false` builds and uploads the site for you to host |
| `pages-url` | project Pages URL | set this for a custom domain |
| `run-linter` | `true` | run `flatpak-builder-lint` |
| `cache` | `true` | cache flatpak runtimes and builder files |
| `builder-args` | `--install-deps-from=flathub` | extra `flatpak-builder` flags, one per line; installs the manifest's declared deps from Flathub |
| `registry` | `ghcr.io` | OCI registry host for the image blobs |
| `oci-repository` | this repository | image repository path within the registry |
| `remote-name` | repo slug `-` | Flatpak remote name and `.flatpakrepo` filename; override for a friendlier name |
| `signing` | `auto` | sign images: `auto` (sign when a key is set), `gpg`, or `off` (see [Signing](#signing-optional)) |
| `runtime-repo` | Flathub `.flatpakrepo` | `RuntimeRepo` in each generated `.flatpakref`; empty omits it |
| `landing-page` | `true` | write the static `index.html`; `false` to render your own page from `index/static` |
| `artifact-name` | `aetherpak-site` | name of the uploaded site artifact when `deploy: false` |
| `concurrency-group` | per repository | override the publish lock; set only if a repo publishes to several independent sites |

Secrets `gpg-private-key` and `gpg-private-key-passphrase` enable image signing.
See [Signing](#signing-optional).

## Publishing multiple apps

One repository can host many apps in a single index. Declare them in
`aetherpak.yaml` and call the same workflow with `config` instead of
`manifest-path`:

```yaml
# .github/workflows/publish.yml
name: Publish Flatpaks
on: { push: { branches: [main] } }
permissions: { contents: read, packages: write, pages: write, id-token: write }
jobs:
publish:
uses: aetherpak/actions/.github/workflows/publish.yml@v3
with:
config: aetherpak.yaml
```

`aetherpak.yaml` lists each app (manifest or prebuilt bundle), its runtime, and
its architectures; the workflow plans the changed apps, builds them in parallel,
and merges them into one shared index. See [ARCHITECTURE.md](ARCHITECTURE.md) for
the schema. Change detection rebuilds only apps whose manifest directory or
config entry changed since the previous commit.

## Installing published apps

The landing page lists each app with its channels. Each release has an **Install**
button that downloads a per-app `.flatpakref` (`refs/-.flatpakref`);
opening it adds the remote and installs the app in one step. When signing is
enabled the ref is verified (it embeds the key and signature lookaside), so the
install is verified too.

To add the whole repository from the command line instead (the remote is named
`-` by default; override with `remote-name`):

```bash
# unsigned, or older clients (< 1.17):
flatpak remote-add --if-not-exists --user --no-gpg-verify \
- oci+https://.github.io/

# signed (flatpak >= 1.17): verified, no key fetch needed
flatpak remote-add --user \
--signature-lookaside=https://.github.io//sigs \
- https://.github.io//-.flatpakrepo

flatpak install --user - org.example.App
```

A `-.flatpakrepo` (linked from the landing page) configures the remote
for every app and channel. When signing is on it embeds the public key, but a
`.flatpakrepo` cannot carry the signature lookaside, so adding it through a GUI
installer leaves verification incomplete until you run the `remote-modify` command
the landing page shows after download:

```bash
flatpak remote-modify --user \
--signature-lookaside=https://.github.io//sigs -
```

Each `.flatpakref` defaults its `RuntimeRepo` to Flathub so installing from it can
pull the app's runtime; set the `runtime-repo` input to change or empty it.

## Signing (optional)

Signing is optional. With no key configured, repositories behave as above
(`--no-gpg-verify`). Configure a GPG key and each pushed OCI image is signed; the
signature, public key, and `sigs/signing.json` are published alongside the index,
and clients can install with verification.

1. Generate a key (CI keys are typically passphrase-less):

```bash
gpg --batch --gen-key <-` or by digest), then Delete version. No token needed.
- GHCR (CLI): `gh api -X DELETE /orgs/OWNER/packages/container/PKG/versions/ID`
with a token that has `delete:packages`.
- Registries that support the OCI delete API: `skopeo login REGISTRY` then
`skopeo delete docker://REGISTRY/NAME@DIGEST`.

The index serves the latest image per channel, so reconcile only removes entries
whose image is genuinely gone.

Pass `reconcile-only: true` (workflow dispatch) to skip every build and just
reconcile against the registry — useful when an image is deleted and you only
need the listing to catch up.

## Standalone actions

The pipeline is also available as composite actions for custom workflows. These
delegate to the [`aetherpak` CLI](https://github.com/aetherpak/cli), so a custom
workflow must put it on `PATH` first with `aetherpak/setup-cli` (the reusable
workflow runs in a container that already bundles it):

- `aetherpak/actions`: the root composite that chains `build` then `publish` in a
single step. Best for prebuilt inputs (a `.flatpak` bundle or OSTree repo) on a
standard runner; manifest builds should use the reusable workflow, which
supplies the builder container.
- `aetherpak/actions/build`: build a manifest with `flatpak-builder`, or import
a prebuilt `.flatpak` bundle or OSTree repository.
- `aetherpak/actions/publish`: push an OSTree repo to GHCR, merge the index, and
write the site. It works on its own with a `repo-path` (your own OSTree repo)
or `bundle-path` (a `.flatpak`), so you can publish output from any toolchain
(for example a Rust/Tauri build) without the `build` action.

The reusable workflow pushes blobs to GHCR. To target another registry, call
`publish` directly with `registry`, `oci-repository`, and `registry-token` (add
`insecure-registry: true` for a local or HTTP registry):

```yaml
- uses: aetherpak/setup-cli@v1 # installs the aetherpak CLI on PATH
- uses: aetherpak/actions/publish@v3
with:
repo-path: _repo # or bundle-path: app.flatpak
registry: registry.example.com
oci-repository: my-org/my-app
registry-token: ${{ secrets.REGISTRY_TOKEN }}
pages-url: https://flatpak.example.com
```

## More

- [ARCHITECTURE.md](ARCHITECTURE.md): how the pieces fit together, what it relies
on, and its limitations.
- [CONTRIBUTING.md](CONTRIBUTING.md): developing and testing the actions.

## License

MIT. See [LICENSE](LICENSE).