{"id":50266232,"url":"https://github.com/aetherpak/actions","last_synced_at":"2026-06-03T14:00:39.177Z","repository":{"id":360098583,"uuid":"1248573401","full_name":"aetherpak/actions","owner":"aetherpak","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-31T01:57:41.000Z","size":503,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T03:19:10.282Z","etag":null,"topics":["flatpak","flatpak-applications","flatpak-builder","flatpak-repository"],"latest_commit_sha":null,"homepage":"http://aetherpak.github.io/actions/","language":"Makefile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aetherpak.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-24T20:22:23.000Z","updated_at":"2026-05-31T01:57:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/aetherpak/actions","commit_stats":null,"previous_names":["aetherpak/actions"],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/aetherpak/actions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aetherpak%2Factions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aetherpak%2Factions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aetherpak%2Factions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aetherpak%2Factions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aetherpak","download_url":"https://codeload.github.com/aetherpak/actions/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aetherpak%2Factions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33867802,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flatpak","flatpak-applications","flatpak-builder","flatpak-repository"],"created_at":"2026-05-27T14:01:35.767Z","updated_at":"2026-06-03T14:00:38.897Z","avatar_url":"https://github.com/aetherpak.png","language":"Makefile","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AetherPak Actions\n\nGitHub Actions that build Flatpak applications and host them as a Flatpak\nrepository, using GitHub Container Registry (GHCR) for the package blobs and\nGitHub Pages for a small registry index and a landing page.\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://aetherpak.github.io/actions-demo/\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/aetherpak/actions/main/docs/site/preview.png\" alt=\"AetherPak registry landing page with one-click Flatpak install\" width=\"480\"\u003e\n  \u003c/a\u003e\n  \u003cbr\u003e\n  \u003cem\u003eThe page AetherPak deploys. Single-app: \u003ca href=\"https://aetherpak.github.io/actions-demo/\"\u003elive demo\u003c/a\u003e · \u003ca href=\"https://github.com/aetherpak/actions-demo\"\u003erepo\u003c/a\u003e. Multi-app: \u003ca href=\"https://abn.github.io/flatpakrepo/\"\u003elive demo\u003c/a\u003e · \u003ca href=\"https://github.com/abn/flatpakrepo\"\u003erepo\u003c/a\u003e\u003c/em\u003e\n\u003c/p\u003e\n\n## Why OCI + Pages\n\nA static-HTTP OSTree repository serves an app as many small objects, so clients\nmake hundreds of requests. That is slow, and it trips GitHub Pages rate limits.\nAetherPak uses Flatpak's native OCI support instead:\n\n- Application layers (blobs) live in GHCR as OCI images.\n- A small JSON index (`index/static`) and a landing page are served from Pages.\n- Clients read the index from Pages and pull layers from GHCR in large chunks.\n\n## Quick start\n\n1. Enable Pages: repository Settings, then Pages, then Source: **GitHub Actions**.\n2. Add `.github/workflows/publish.yml`:\n\n```yaml\nname: Publish Flatpak\non: { push: { branches: [main] } }\npermissions:\n  contents: read\n  packages: write\n  pages: write\n  id-token: write\njobs:\n  publish:\n    uses: aetherpak/actions/.github/workflows/publish.yml@v3\n    with:\n      manifest-path: org.example.App.json\n```\n\nBy default the app is built for `x86_64` and `aarch64` and deployed to Pages.\n\nAfter the first run, make the GHCR package public so users can install without\nauthenticating: the package's page, then **Package settings**, then **Change\nvisibility**, then **Public**.\n\nFor an organization repository, an owner must first allow public packages,\notherwise the image is created private and can't be switched. Do this **before**\nthe first publish: **Organization → Settings → Packages → Package creation**,\nthen enable **Public**.\n\n### Options\n\n| Input | Default | Purpose |\n|---|---|---|\n| `manifest-path` | _(required)_ | Flatpak manifest to build |\n| `arches` | `x86_64 aarch64` | architectures to build |\n| `branch` | `stable` on tags, `beta` on the default branch, else the ref name | Flatpak branch (channel) |\n| `deploy` | `true` | deploy to Pages; `false` builds and uploads the site for you to host |\n| `pages-url` | project Pages URL | set this for a custom domain |\n| `run-linter` | `true` | run `flatpak-builder-lint` |\n| `cache` | `true` | cache flatpak runtimes and builder files |\n| `builder-args` | `--install-deps-from=flathub` | extra `flatpak-builder` flags, one per line; installs the manifest's declared deps from Flathub |\n| `registry` | `ghcr.io` | OCI registry host for the image blobs |\n| `oci-repository` | this repository | image repository path within the registry |\n| `remote-name` | repo slug `\u003cowner\u003e-\u003crepo\u003e` | Flatpak remote name and `.flatpakrepo` filename; override for a friendlier name |\n| `signing` | `auto` | sign images: `auto` (sign when a key is set), `gpg`, or `off` (see [Signing](#signing-optional)) |\n| `runtime-repo` | Flathub `.flatpakrepo` | `RuntimeRepo` in each generated `.flatpakref`; empty omits it |\n| `landing-page` | `true` | write the static `index.html`; `false` to render your own page from `index/static` |\n| `artifact-name` | `aetherpak-site` | name of the uploaded site artifact when `deploy: false` |\n| `concurrency-group` | per repository | override the publish lock; set only if a repo publishes to several independent sites |\n\nSecrets `gpg-private-key` and `gpg-private-key-passphrase` enable image signing.\nSee [Signing](#signing-optional).\n\n## Publishing multiple apps\n\nOne repository can host many apps in a single index. Declare them in\n`aetherpak.yaml` and call the same workflow with `config` instead of\n`manifest-path`:\n\n```yaml\n# .github/workflows/publish.yml\nname: Publish Flatpaks\non: { push: { branches: [main] } }\npermissions: { contents: read, packages: write, pages: write, id-token: write }\njobs:\n  publish:\n    uses: aetherpak/actions/.github/workflows/publish.yml@v3\n    with:\n      config: aetherpak.yaml\n```\n\n`aetherpak.yaml` lists each app (manifest or prebuilt bundle), its runtime, and\nits architectures; the workflow plans the changed apps, builds them in parallel,\nand merges them into one shared index. See [ARCHITECTURE.md](ARCHITECTURE.md) for\nthe schema. Change detection rebuilds only apps whose manifest directory or\nconfig entry changed since the previous commit.\n\n## Installing published apps\n\nThe landing page lists each app with its channels. Each release has an **Install**\nbutton that downloads a per-app `.flatpakref` (`refs/\u003capp\u003e-\u003cchannel\u003e.flatpakref`);\nopening it adds the remote and installs the app in one step. When signing is\nenabled the ref is verified (it embeds the key and signature lookaside), so the\ninstall is verified too.\n\nTo add the whole repository from the command line instead (the remote is named\n`\u003cowner\u003e-\u003crepo\u003e` by default; override with `remote-name`):\n\n```bash\n# unsigned, or older clients (\u003c 1.17):\nflatpak remote-add --if-not-exists --user --no-gpg-verify \\\n  \u003cowner\u003e-\u003crepo\u003e oci+https://\u003cowner\u003e.github.io/\u003crepo\u003e\n\n# signed (flatpak \u003e= 1.17): verified, no key fetch needed\nflatpak remote-add --user \\\n  --signature-lookaside=https://\u003cowner\u003e.github.io/\u003crepo\u003e/sigs \\\n  \u003cowner\u003e-\u003crepo\u003e https://\u003cowner\u003e.github.io/\u003crepo\u003e/\u003cowner\u003e-\u003crepo\u003e.flatpakrepo\n\nflatpak install --user \u003cowner\u003e-\u003crepo\u003e org.example.App\n```\n\nA `\u003cowner\u003e-\u003crepo\u003e.flatpakrepo` (linked from the landing page) configures the remote\nfor every app and channel. When signing is on it embeds the public key, but a\n`.flatpakrepo` cannot carry the signature lookaside, so adding it through a GUI\ninstaller leaves verification incomplete until you run the `remote-modify` command\nthe landing page shows after download:\n\n```bash\nflatpak remote-modify --user \\\n  --signature-lookaside=https://\u003cowner\u003e.github.io/\u003crepo\u003e/sigs \u003cowner\u003e-\u003crepo\u003e\n```\n\nEach `.flatpakref` defaults its `RuntimeRepo` to Flathub so installing from it can\npull the app's runtime; set the `runtime-repo` input to change or empty it.\n\n## Signing (optional)\n\nSigning is optional. With no key configured, repositories behave as above\n(`--no-gpg-verify`). Configure a GPG key and each pushed OCI image is signed; the\nsignature, public key, and `sigs/signing.json` are published alongside the index,\nand clients can install with verification.\n\n1. Generate a key (CI keys are typically passphrase-less):\n\n   ```bash\n   gpg --batch --gen-key \u003c\u003cEOF\n   %no-protection\n   Key-Type: RSA\n   Key-Length: 4096\n   Name-Real: Example Releases\n   Name-Email: releases@example.org\n   Expire-Date: 0\n   %commit\n   EOF\n   gpg --armor --export-secret-keys releases@example.org\n   ```\n\n2. Store the armored private key as the repository **secret**\n   `gpg-private-key` (and `gpg-private-key-passphrase` if protected), then pass\n   it to the workflow:\n\n   ```yaml\n   jobs:\n     publish:\n       uses: aetherpak/actions/.github/workflows/publish.yml@v3\n       with:\n         manifest-path: org.example.App.json\n         signing: gpg\n       secrets:\n         gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}\n   ```\n\n3. Install with verification. With a key set, the landing page shows the verified\n   `remote-add` for your repository (the signed commands under\n   [Installing published apps](#installing-published-apps)). Clients on flatpak\n   \u003c 1.17 cannot read the lookaside and fall back to the `--no-gpg-verify`\n   command, also shown on the landing page.\n\n**Rotation:** generate a new key, replace the secret, and re-publish **every\nchannel**. Each publish re-signs the image it pushes, and rotation only fully\ntakes effect once every image still listed in the index has been re-signed with\nthe new key. The new public key replaces the old one on the next deploy.\n\n## Host it yourself\n\nSet `deploy: false` to keep AetherPak off your Pages site. The workflow then\nuploads the built site as an `aetherpak-site` artifact and skips deployment. Serve\nthat artifact yourself (under a subpath of an existing Pages site, or on external\nhosting) and set `pages-url` to the final URL so the index and `.flatpakrepo`\nreference it. Use `landing-page: false` to skip `index.html` and render your own\npage from `index/static`.\n\n## Maintaining the repository\n\nTo remove an app, channel, or architecture, delete its image from the registry\nand re-run the publish workflow. Every publish reconciles `index/static` against\nthe registry and drops entries whose image no longer exists, so the listing\ndisappears on the next run.\n\nDelete the image with whatever your registry supports:\n\n- GHCR (web UI): your profile or org, then Packages, the package, the version\n  (tagged `\u003cbranch\u003e-\u003carch\u003e` or by digest), then Delete version. No token needed.\n- GHCR (CLI): `gh api -X DELETE /orgs/OWNER/packages/container/PKG/versions/ID`\n  with a token that has `delete:packages`.\n- Registries that support the OCI delete API: `skopeo login REGISTRY` then\n  `skopeo delete docker://REGISTRY/NAME@DIGEST`.\n\nThe index serves the latest image per channel, so reconcile only removes entries\nwhose image is genuinely gone.\n\nPass `reconcile-only: true` (workflow dispatch) to skip every build and just\nreconcile against the registry — useful when an image is deleted and you only\nneed the listing to catch up.\n\n## Standalone actions\n\nThe pipeline is also available as composite actions for custom workflows. These\ndelegate to the [`aetherpak` CLI](https://github.com/aetherpak/cli), so a custom\nworkflow must put it on `PATH` first with `aetherpak/setup-cli` (the reusable\nworkflow runs in a container that already bundles it):\n\n- `aetherpak/actions`: the root composite that chains `build` then `publish` in a\n  single step. Best for prebuilt inputs (a `.flatpak` bundle or OSTree repo) on a\n  standard runner; manifest builds should use the reusable workflow, which\n  supplies the builder container.\n- `aetherpak/actions/build`: build a manifest with `flatpak-builder`, or import\n  a prebuilt `.flatpak` bundle or OSTree repository.\n- `aetherpak/actions/publish`: push an OSTree repo to GHCR, merge the index, and\n  write the site. It works on its own with a `repo-path` (your own OSTree repo)\n  or `bundle-path` (a `.flatpak`), so you can publish output from any toolchain\n  (for example a Rust/Tauri build) without the `build` action.\n\nThe reusable workflow pushes blobs to GHCR. To target another registry, call\n`publish` directly with `registry`, `oci-repository`, and `registry-token` (add\n`insecure-registry: true` for a local or HTTP registry):\n\n```yaml\n- uses: aetherpak/setup-cli@v1   # installs the aetherpak CLI on PATH\n- uses: aetherpak/actions/publish@v3\n  with:\n    repo-path: _repo            # or bundle-path: app.flatpak\n    registry: registry.example.com\n    oci-repository: my-org/my-app\n    registry-token: ${{ secrets.REGISTRY_TOKEN }}\n    pages-url: https://flatpak.example.com\n```\n\n## More\n\n- [ARCHITECTURE.md](ARCHITECTURE.md): how the pieces fit together, what it relies\n  on, and its limitations.\n- [CONTRIBUTING.md](CONTRIBUTING.md): developing and testing the actions.\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faetherpak%2Factions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faetherpak%2Factions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faetherpak%2Factions/lists"}