Skip to content
This repository was archived by the owner on Nov 3, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Lint the action, and run it on real runners in each mode
---
name: Test

'on':
pull_request:
types: [opened, reopened, synchronize]
push:
branches: [main]

jobs:
lint:
name: 'Lint'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Run actionlint
run: |
bash <(curl -s https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
./actionlint -color
- name: Run shellcheck
run: shellcheck setup-vagrant-libvirt.bash

# cache-only first, so the install jobs that follow can restore its packages
cache:
name: 'Cache-only (${{ matrix.runner }})'
strategy:
matrix:
runner: [ubuntu-24.04, ubuntu-latest]
fail-fast: false
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v7
- id: vagrant
uses: ./
with:
cache-only: 'true'
- name: Check the result
env:
SOURCE: ${{ steps.vagrant.outputs.source }}
run: |
echo "source: ${SOURCE}"
. /etc/os-release
if [ "$VERSION_CODENAME" = 'noble' ]; then
[[ "$SOURCE" =~ ^(cached|saved)$ ]]
else
[ "$SOURCE" = 'none' ]
fi
! command -v vagrant

# `noble` is the source expected where the runner is on noble, which the
# PPA publishes for; on any other series it is always hashicorp
install:
name: 'Install (${{ matrix.runner }}, cache ${{ matrix.cache }}, Launchpad ${{ matrix.launchpad }})'
needs: cache
strategy:
matrix:
include:
- {runner: ubuntu-24.04, cache: 'true', launchpad: reachable, noble: cache}
- {runner: ubuntu-24.04, cache: 'true', launchpad: blocked, noble: cache}
- {runner: ubuntu-24.04, cache: 'false', launchpad: reachable, noble: ppa}
- {runner: ubuntu-24.04, cache: 'false', launchpad: blocked, noble: hashicorp}
- {runner: ubuntu-latest, cache: 'true', launchpad: reachable, noble: cache}
fail-fast: false
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v7
- name: Block Launchpad
if: matrix.launchpad == 'blocked'
run: |
for host in ppa.launchpadcontent.net ppa.launchpad.net api.launchpad.net launchpad.net keyserver.ubuntu.com; do
echo "0.0.0.0 ${host}" | sudo tee -a /etc/hosts
done
- id: vagrant
uses: ./
with:
cache: ${{ matrix.cache }}
- name: Check the result
env:
SOURCE: ${{ steps.vagrant.outputs.source }}
NOBLE: ${{ matrix.noble }}
run: |
. /etc/os-release
expected=hashicorp
if [ "$VERSION_CODENAME" = 'noble' ]; then
expected=$NOBLE
fi
echo "source: ${SOURCE} (expected ${expected})"
[ "$SOURCE" = "$expected" ]
vagrant --version
vagrant plugin list | grep vagrant-libvirt
virsh --connect qemu:///system list --all

# The one check for branch protection to require: GitHub only blocks a
# merge on checks listed by name, and this job fails unless every job it
# needs succeeded. Add any new job in this workflow to `needs:`.
all-checks:
name: 'All checks'
if: always()
needs:
- lint
- cache
- install
runs-on: ubuntu-latest
steps:
- name: 'Fail unless every needed job succeeded'
env:
NEEDS: ${{ toJSON(needs) }}
run: |
jq -r 'to_entries[] | "\(.key): \(.value.result)"' <<< "$NEEDS"
jq -e 'all(.[]; .result == "success")' <<< "$NEEDS" > /dev/null
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [1.0.0]

### Added

- Install Vagrant, vagrant-libvirt and libvirt from the Actions cache,
`ppa:evgeni/vagrant`, or HashiCorp's apt repository, in that order.
- `cache-only` mode, to save the PPA's packages from the default branch.

[1.0.0]: https://github.com/simp/github-action-setup-vagrant-libvirt/releases/tag/v1.0.0
90 changes: 89 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,90 @@
# github-action-setup-vagrant-libvirt
Install Vagrant and vagrant-libvirt on Ubuntu GitHub Actions runners, surviving Launchpad PPA outages

Install Vagrant, the vagrant-libvirt provider and libvirt on an Ubuntu
GitHub Actions runner, for Beaker acceptance tests with
`BEAKER_HYPERVISOR=vagrant_libvirt`.

Ubuntu stopped shipping `vagrant` and `vagrant-libvirt` after 23.10.
[`ppa:evgeni/vagrant`](https://launchpad.net/~evgeni/+archive/ubuntu/vagrant)
carries matching builds of both, with Vagrant 2.3.7, the last MPL-licensed
release. Installing from the PPA alone fails the job whenever Launchpad is
down, so this action tries, in order:

1. **The Actions cache**: the PPA's packages, saved by a `cache-only` run on
the default branch. No request goes to Launchpad. Their dependencies come
from the Ubuntu archive.
2. **The PPA**: `add-apt-repository`, then a refresh of the PPA's index alone
and the install. Each step is tried three times, 30s and then 60s apart.
3. **HashiCorp**: `vagrant` from <https://apt.releases.hashicorp.com>, pinned
to `vagrant-version`, and `vagrant plugin install vagrant-libvirt`, pinned
to `vagrant-libvirt-version` and built against `libvirt-dev`. This is
BSL-licensed Vagrant, so the job gets a warning.

On an Ubuntu series the PPA doesn't publish for (`ppa-series`), the action
goes straight to HashiCorp with a notice.

## Usage

In the acceptance job:

```yaml
- name: Setup libvirt for Vagrant
uses: simp/github-action-setup-vagrant-libvirt@v1.0.0
- name: beaker
env:
BEAKER_HYPERVISOR: 'vagrant_libvirt'
VAGRANT_DEFAULT_PROVIDER: 'libvirt'
run: bundle exec rake beaker:suites[default,almalinux9]
```

GitHub only lets a pull request restore caches saved from its own ref, its
base branch or the default branch, so a cache saved by one pull request is
no use to the next. Save the packages from the default branch with a
separate workflow, using a runner on a series the PPA publishes for:

```yaml
name: Vagrant package cache

'on':
push:
branches: [main]
schedule:
- cron: '17 6 * * 1,4'
workflow_dispatch: {}

jobs:
cache:
runs-on: ubuntu-24.04
steps:
- uses: simp/github-action-setup-vagrant-libvirt@v1.0.0
with:
cache-only: 'true'
```

A `cache-only` run looks up the packages the PPA currently provides and
derives the cache key from their file names. If that key is already cached,
it restores it, which also keeps GitHub from evicting it after seven days
without use. Otherwise it downloads the packages and saves them. A new PPA
build gets a new key, and installs restore the newest key for their Ubuntu
series and architecture. The run fails if the PPA can't be reached after
the retries.

## Inputs

| Input | Default | Description |
|---|---|---|
| `cache-only` | `'false'` | `'true'` saves the PPA's packages to the Actions cache instead of installing anything |
| `cache` | `'true'` | `'false'` ignores the Actions cache when installing |
| `ppa-series` | `'noble'` | Space-separated Ubuntu codenames the PPA publishes for |
| `vagrant-version` | `'2.4.9-1'` | HashiCorp's apt version of `vagrant`, for the fallback |
| `vagrant-libvirt-version` | `'0.12.2'` | The `vagrant-libvirt` plugin version for the fallback |

## Outputs

| Output | Description |
|---|---|
| `source` | Install: `cache`, `ppa` or `hashicorp`. Cache-only: `cached` (already cached), `saved`, or `none` (the PPA doesn't publish for this series) |

## License

[Apache-2.0](LICENSE)
126 changes: 126 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
name: 'Setup Vagrant with libvirt'
description: >-
Install Vagrant, vagrant-libvirt and libvirt on an Ubuntu runner from
ppa:evgeni/vagrant, the Actions cache, or HashiCorp's apt repository,
so a Launchpad outage doesn't fail the job.
branding:
icon: 'box'
color: 'blue'

inputs:
cache-only:
description: >-
'true' to save the PPA's packages to the Actions cache instead of
installing anything. Run it from the default branch (push, schedule)
so pull requests can restore the cache.
required: false
default: 'false'
cache:
description: "'false' to ignore the Actions cache when installing"
required: false
default: 'true'
ppa-series:
description: >-
Space-separated Ubuntu codenames that ppa:evgeni/vagrant publishes
for. On any other series the action goes straight to HashiCorp.
required: false
default: 'noble'
vagrant-version:
description: "HashiCorp's apt version of vagrant, used when the PPA can't be"
required: false
default: '2.4.9-1'
vagrant-libvirt-version:
description: 'The vagrant-libvirt plugin version installed with HashiCorp vagrant'
required: false
default: '0.12.2'

outputs:
source:
description: >-
Where Vagrant came from: 'cache', 'ppa' or 'hashicorp' (install), or
'cached', 'saved' or 'none' (cache-only)
value: ${{ steps.install.outputs.source || steps.result.outputs.source }}

runs:
using: composite
steps:
- name: Check the Ubuntu series
id: series
shell: bash
env:
PPA_SERIES: ${{ inputs.ppa-series }}
run: '"$GITHUB_ACTION_PATH/setup-vagrant-libvirt.bash" series'

# Install: restore the newest cached package set, if any
- name: Restore the cached PPA packages
if: >-
inputs.cache-only != 'true' && inputs.cache != 'false' &&
steps.series.outputs.ppa == 'true'
uses: actions/cache/restore@v6
with:
path: ${{ steps.series.outputs.dir }}
key: ${{ steps.series.outputs.prefix }}
restore-keys: ${{ steps.series.outputs.prefix }}

- name: Install Vagrant and libvirt
id: install
if: inputs.cache-only != 'true'
shell: bash
env:
DEBS: ${{ steps.series.outputs.dir }}
PPA: ${{ steps.series.outputs.ppa }}
CODENAME: ${{ steps.series.outputs.codename }}
VAGRANT_VERSION: ${{ inputs.vagrant-version }}
VAGRANT_LIBVIRT_VERSION: ${{ inputs.vagrant-libvirt-version }}
run: '"$GITHUB_ACTION_PATH/setup-vagrant-libvirt.bash" install'

# Cache-only: name the PPA's current packages, then save them once
- name: Resolve the PPA packages
id: resolve
if: inputs.cache-only == 'true' && steps.series.outputs.ppa == 'true'
shell: bash
env:
PREFIX: ${{ steps.series.outputs.prefix }}
run: '"$GITHUB_ACTION_PATH/setup-vagrant-libvirt.bash" resolve'

# A restore of an existing key also keeps it from being evicted
- name: Check the cache
id: cached
if: steps.resolve.outcome == 'success'
uses: actions/cache/restore@v6
with:
path: ${{ steps.series.outputs.dir }}
key: ${{ steps.resolve.outputs.key }}

- name: Download the PPA packages
if: steps.resolve.outcome == 'success' && steps.cached.outputs.cache-hit != 'true'
shell: bash
env:
DEBS: ${{ steps.series.outputs.dir }}
run: '"$GITHUB_ACTION_PATH/setup-vagrant-libvirt.bash" download'

- name: Save the PPA packages
if: steps.resolve.outcome == 'success' && steps.cached.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: ${{ steps.series.outputs.dir }}
key: ${{ steps.resolve.outputs.key }}

- name: Report the cache result
id: result
if: inputs.cache-only == 'true'
shell: bash
env:
PPA: ${{ steps.series.outputs.ppa }}
CODENAME: ${{ steps.series.outputs.codename }}
HIT: ${{ steps.cached.outputs.cache-hit }}
run: |
if [ "$PPA" != 'true' ]; then
echo "::notice::ppa:evgeni/vagrant has no packages for Ubuntu ${CODENAME}; nothing to cache"
echo 'source=none' >> "$GITHUB_OUTPUT"
elif [ "$HIT" = 'true' ]; then
echo 'source=cached' >> "$GITHUB_OUTPUT"
else
echo 'source=saved' >> "$GITHUB_OUTPUT"
fi
7 changes: 7 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
"github>simp/renovate-config:ruby.json"
]
}
Loading