Skip to content

Repository files navigation

Zero Install GitHub Actions

Reusable GitHub Actions for publishing software with Zero Install, wrapping the 0template, 0publish and 0repo tools.

All actions run on Linux, macOS and Windows runners and download the tools they need on-demand.

ActionPurpose
setupMake the 0install command available to subsequent steps
0templateGenerate a feed (and archive) for a specific version from a template
0publishEdit a feed in place, e.g. add a new version to it or sign it
0repo-setupCheck out a 0repo repository into the directory structure 0repo expects
0repoMerge incoming feeds into the repository, sign them and regenerate the catalog
0repo-submitTrigger the workflow of a 0repo repository to pick up a new release

Usage

setup

Makes the 0install command available to subsequent steps, downloading it if it is not already on the PATH. The other actions do this for themselves, so you only need this if you want to run 0install commands yourself.

- uses: 0install/github-actions/setup@v1
- run: 0install run https://apps.0install.net/0install/0test.xml myapp.xml

0template

Runs 0template on a *.xml.template file, producing a feed for a single version. If the template uses local-path with a relative href, the archive is generated too.

- uses: 0install/github-actions/0template@v1id: feedwith:
template: myapp.xml.templateversion: '1.2.3'
InputDescription
templateThe path of the *.xml.template file. Required.
versionThe version number of the release. Required. Write in quotes to avoid accidental interpretation as a floating point number (e.g., '1.0' instead of 1.0).
argsAdditional whitespace-separated key=value parameters for the template, e.g. released=2026-08-02.
archive-urlThe URL the generated archive will be published at. Relative hrefs in the generated feed are replaced with this URL. A value ending in / is treated as a directory and the archive's file name is appended.
github-releaseSet to true if the generated archive will be attached to the GitHub Release for the current tag. Shorthand for archive-url: https://github.com/OWNER/REPO/releases/download/TAG/.
upload-artifactSet to false to keep the generated feed and archives from being uploaded as a GitHub Actions artifact. Defaults to true.
artifact-nameThe name of that artifact. Defaults to feed. Artifact names must be unique within a workflow run, so override this if you run the action more than once (e.g. in a matrix job).
OutputDescription
feedThe path of the generated feed, e.g. myapp-1.2.3.xml.
archiveThe paths of the generated archives, one per line. Empty if the feed references external archives.

0template writes its output next to the template and resolves local-path relative to it, so a template in a subdirectory produces its feed in that subdirectory. The outputs are relative to the workspace either way, so they can be passed straight to other actions:

- uses: softprops/action-gh-release@v3with:
files: | ${{steps.feed.outputs.feed}} ${{steps.feed.outputs.archive}}

The files are also uploaded as a GitHub Actions artifact, so they can be inspected or downloaded by a later job even on builds that do not publish anything.

Feeds usually need to point at an absolute URL, while 0template needs a relative href to generate the archive. Use archive-url to bridge the two, or github-release if the archive is going to be an asset of the GitHub Release for the current tag:

- uses: 0install/github-actions/0template@v1with:
template: myapp.xml.templateversion: ${{github.ref_name}}github-release: true

is equivalent to:

archive-url: https://github.com/${{github.repository}}/releases/download/${{github.ref_name}}/

0publish

Runs 0publish on a feed, editing it in place. Typically used to merge a newly generated per-version feed into a master feed that lists all versions.

- uses: 0install/github-actions/0publish@v1with:
feed: gh-pages/myapp.xmladd-from: ${{steps.feed.outputs.feed}}gpg-key: ${{secrets.GPG_KEY}}
InputDescription
feedThe path of the feed to edit. Required.
add-fromThe path or URL of a feed whose implementations should be added to the feed.
set-releasedThe release date to set in the format YYYY-MM-DD (or today).
set-stabilityThe stability rating to set (stable, testing, developer, ...).
keyThe ID of the GnuPG key to sign with. Defaults to the key the feed was previously signed with.
xmlsignSet to true to add a signature to a previously unsigned feed and export the public key next to it.
gpg-keyAn ASCII-armored GnuPG secret key to import before running 0publish.
argsAdditional whitespace-separated arguments to pass to 0publish.

If the feed was already signed, 0publish resigns it with the same key, so gpg-key needs to hold that key. Export it with gpg --export-secret-keys --armor YOURKEY and store it as a repository secret. The key must not be protected by a passphrase.

0repo-setup

Checks out a 0repo repository into the directory structure 0repo expects.

- uses: 0install/github-actions/0repo-setup@v1

This replaces the actions/checkout step that usually sits at the top of a workflow. 0repo works on two branches of the same repository at once, and needs them next to each other rather than at the workspace root, so this action performs both checkouts itself and creates the scaffolding around them:

feeds/ # default branch: 0repo-config.py, the unsigned feeds and templates
public/ # gh-pages branch: the published (signed) feeds and archives.db
incoming/ # empty, holds feeds waiting to be merged
0repo-config.py # symlink to feeds/0repo-config.py
archives.db # symlink to public/archives.db

Later steps in the job therefore work inside these directories rather than at the workspace root.

InputDescription
repositoryThe repository holding the 0repo configuration and the unsigned feeds. Defaults to the current repository.
refThe branch, tag or SHA to check out the feeds from. Defaults to the default branch.
public-refThe branch holding the published (signed) feeds. Defaults to gh-pages.
tokenThe token to use for checking out and (later) pushing the repository. Defaults to github.token.
git-user-name, git-user-emailThe identity to use for Git commits.

0repo

Runs 0repo, which validates the feeds in incoming/, merges them into the master feeds, signs everything and regenerates the catalog.

- uses: 0install/github-actions/0repo@v1with:
gpg-key: ${{secrets.GPG_KEY}}
InputDescription
incoming-feed-urlThe URL of a feed to download into incoming/ before running 0repo.
incoming-archive-urlThe URL of an archive to register in archives.db.
gpg-keyAn ASCII-armored GnuPG secret key to sign the feeds with.
push-feedsSet to true to push the merged feeds in feeds/ back to the repository.
push-publicSet to true to push the published feeds in public/ back to the repository.
working-directoryThe directory containing the 0repo directory structure. Defaults to the workspace root.
argsAdditional whitespace-separated arguments to pass to 0repo.

0repo commits its results to both checkouts made by 0repo-setup: the published feeds to public/ and the merged feeds to feeds/.

By default it leaves those commits sitting in the workspace, which is what you want when validating a pull request, or when the changes are going into a pull request of their own. You can configure the action to push them for you, so the workflow needs no git push step of its own:

- uses: 0install/github-actions/0repo@v1with:
gpg-key: ${{secrets.GPG_KEY}}push-feeds: truepush-public: true

If gpg-key is not set, 0repo runs with the environment variable NO_SIGN set. Have your 0repo-config.py honor it, so that pull requests can be validated without exposing the production key:

GPG_SIGNING_KEY=Noneifos.getenv('NO_SIGN') else"0xYOURKEYFINGERPRINT"

Such a run produces unsigned feeds, which must never end up on the published branch. public/ is therefore only pushed when gpg-key is set, even if push-public is true.

incoming-feed-url fetches a feed published elsewhere into incoming/, so a single step covers the whole "a new release arrived" case:

- uses: 0install/github-actions/0repo@v1with:
incoming-feed-url: ${{inputs.feed_url}}incoming-archive-url: ${{inputs.archive_url}}gpg-key: ${{secrets.GPG_KEY}}push-feeds: truepush-public: true

Archives hosted by the repository itself are handled by 0repo. incoming-archive-url is only needed for archives hosted elsewhere (typically as assets of a GitHub Release in the application's own repository): the archive is downloaded, hashed and registered in archives.db so that 0repo can point the published feed at it.

0repo-submit

Triggers a workflow_dispatch workflow in a 0repo repository, telling it to pick up a newly released feed. Use this in the workflow of an application that publishes into a central feed repository.

- uses: 0install/github-actions/0repo-submit@v1with:
repository: 0install/appsfeed-url: ${{steps.feed.outputs.feed}}archive-url: ${{steps.feed.outputs.archive}}token: ${{secrets.PERSONAL_TOKEN}}
InputDescription
repositoryThe repository running 0repo, e.g. 0install/apps. Required.
feed-urlThe URL of the feed to merge into the repository. Required.
archive-urlThe URL of an archive to register in archives.db.
tokenA token with Actions: write permission for the target repository. Required.
workflowThe name of the workflow to trigger. Defaults to Incoming.
refThe branch or tag to run the workflow from.

feed-url and archive-url may be relative instead of absolute, in which case they are resolved against the GitHub Release for the current tag. The example above is therefore equivalent to:

feed-url: https://github.com/${{github.repository}}/releases/download/${{github.ref_name}}/myapp-1.2.3.xmlarchive-url: https://github.com/${{github.repository}}/releases/download/${{github.ref_name}}/myapp-1.2.3.tar.gz

Since assets of a GitHub Release are flat, only the file name of a relative path is used. That way the paths output by the 0template action can be passed straight through, even for a template in a subdirectory.

The default GITHUB_TOKEN only has permissions for the current repository, so token needs to be a (fine-grained) personal access token stored as a secret.

Examples

Publishing a single feed to GitHub Pages

The master feed lives on the gh-pages branch of the same repository. Each release generates a feed for the new version, merges it into the master feed and pushes it back.

name: Releaseon:
push:
tags: ['*']jobs:
release:
runs-on: ubuntu-latestpermissions: {contents: write}steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v7with: {ref: gh-pages, path: gh-pages}
- name: Buildrun: ./build.sh ${{github.ref_name}}
- name: Generate feedid: feeduses: 0install/github-actions/0template@v1with:
template: myapp.xml.templateversion: ${{github.ref_name}}github-release: true
- name: Create GitHub Releaseuses: softprops/action-gh-release@v3with:
files: | ${{steps.feed.outputs.feed}} ${{steps.feed.outputs.archive}} - name: Add to master feeduses: 0install/github-actions/0publish@v1with:
feed: gh-pages/myapp.xmladd-from: ${{steps.feed.outputs.feed}}gpg-key: ${{secrets.GPG_KEY}}
- name: Push gh-pagesworking-directory: gh-pagesrun: | git config user.name github-actions[bot] git config user.email 41898282+github-actions[bot]@users.noreply.github.com git commit -am "Published ${{github.ref_name}}" git push

Releasing into a 0repo repository

The application's workflow attaches the feed and the archive to a GitHub Release and hands both URLs to the central feed repository:

 - name: Generate feedid: feeduses: 0install/github-actions/0template@v1with:
template: myapp.xml.templateversion: ${{github.ref_name}}github-release: true
- name: Create GitHub Releaseuses: softprops/action-gh-release@v3with:
files: | ${{steps.feed.outputs.feed}} ${{steps.feed.outputs.archive}} - name: Submit feeduses: 0install/github-actions/0repo-submit@v1with:
repository: myname/feedsfeed-url: ${{steps.feed.outputs.feed}}archive-url: ${{steps.feed.outputs.archive}}token: ${{secrets.PERSONAL_TOKEN}}

The matching Incoming workflow in the feed repository:

name: Incomingon:
workflow_dispatch:
inputs:
feed_url: {required: true, description: URL of feed to merge in}archive_url: {required: false, description: URL of archive to add to archives.db}concurrency: {group: publish}jobs:
incoming:
runs-on: ubuntu-lateststeps:
# Checks out the repository; no actions/checkout needed
- uses: 0install/github-actions/0repo-setup@v1
- uses: 0install/github-actions/0repo@v1with:
incoming-feed-url: ${{inputs.feed_url}}incoming-archive-url: ${{inputs.archive_url}}gpg-key: ${{secrets.GPG_KEY}}push-feeds: truepush-public: true

See the packaging tutorials for the bigger picture and 0install/apps for a real-world deployment.

About

Actions for working with 0install feeds in your GitHub CI workflows

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages