348 lines
14 KiB
YAML
348 lines
14 KiB
YAML
name: CI / Build / Publish
|
||
|
||
on:
|
||
push:
|
||
branches: ["**"]
|
||
tags: ["v*.*.*"]
|
||
pull_request:
|
||
branches: [master]
|
||
|
||
env:
|
||
PACKAGE_NAME: freenas-proxmox
|
||
|
||
# Cancel in-flight runs for the same branch on new push
|
||
concurrency:
|
||
group: ${{ github.workflow }}-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
# ── Job 1: Lint ──────────────────────────────────────────────────────────────
|
||
jobs:
|
||
lint:
|
||
name: Lint
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v6
|
||
|
||
- name: Install lint tools
|
||
run: |
|
||
sudo apt-get update -qq
|
||
sudo apt-get install -y \
|
||
shellcheck libperl-critic-perl \
|
||
libwww-perl libio-socket-ssl-perl librest-client-perl libjson-perl
|
||
# PVE::SafeSyslog only exists on Proxmox hosts; stub it for syntax checking
|
||
mkdir -p /tmp/pve-stub/PVE
|
||
printf 'package PVE::SafeSyslog;\nuse Exporter "import";\nour @EXPORT = qw(syslog);\nsub syslog {}\n1;\n' \
|
||
> /tmp/pve-stub/PVE/SafeSyslog.pm
|
||
|
||
- name: Perl syntax check (all modules)
|
||
run: |
|
||
echo "==> Checking Perl syntax..."
|
||
find perl5 stable-*/perl5 -name "*.pm" -print0 \
|
||
| xargs -0 -I{} perl -c -I/tmp/pve-stub {} \
|
||
&& echo "All .pm files OK"
|
||
|
||
- name: Perl static analysis (perlcritic)
|
||
run: |
|
||
echo "==> Running perlcritic..."
|
||
perlcritic --profile .perlcriticrc \
|
||
perl5/PVE/Storage/LunCmd/FreeNAS.pm
|
||
|
||
- name: Shell script lint (shellcheck)
|
||
run: |
|
||
echo "==> Running shellcheck..."
|
||
shellcheck --severity=warning \
|
||
packaging/DEBIAN/postinst \
|
||
packaging/DEBIAN/postrm
|
||
echo "Shell scripts OK"
|
||
|
||
# ── Job 2: Validate patches apply cleanly ──────────────────────────────────
|
||
validate-patches:
|
||
name: Validate Patches
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v6
|
||
|
||
- name: "Dry-run patch: ZFSPlugin (PVE 8)"
|
||
run: |
|
||
patch --dry-run --ignore-whitespace \
|
||
stable-8/perl5/PVE/Storage/ZFSPlugin.pm.orig \
|
||
< stable-8/perl5/PVE/Storage/ZFSPlugin.pm.patch \
|
||
&& echo "ZFSPlugin PVE-8 patch: OK"
|
||
|
||
- name: "Dry-run patch: apidoc.js (PVE 8)"
|
||
run: |
|
||
patch --dry-run --ignore-whitespace \
|
||
stable-8/pve-docs/api-viewer/apidoc.js.orig \
|
||
< stable-8/pve-docs/api-viewer/apidoc.js.patch \
|
||
&& echo "apidoc PVE-8 patch: OK"
|
||
|
||
# ── Job 3: Build .deb ────────────────────────────────────────────────────────
|
||
build:
|
||
name: Build Package
|
||
runs-on: ubuntu-latest
|
||
needs: [lint, validate-patches]
|
||
|
||
outputs:
|
||
version: ${{ steps.vars.outputs.version }}
|
||
deb_file: ${{ steps.vars.outputs.deb_file }}
|
||
channel: ${{ steps.vars.outputs.channel }}
|
||
cloudsmith_repo: ${{ steps.vars.outputs.cloudsmith_repo }}
|
||
is_release: ${{ steps.vars.outputs.is_release }}
|
||
|
||
steps:
|
||
- uses: actions/checkout@v6
|
||
with:
|
||
# Fetch full history so tag-based versioning works
|
||
fetch-depth: 0
|
||
|
||
# ── Version resolution ─────────────────────────────────────────────────
|
||
# Version strategy:
|
||
# Tagged release (v1.2.3) → 1.2.3 (stable channel)
|
||
# master branch → <tag>-beta+<sha> (testing channel)
|
||
# feature_* branch → <tag>-alpha+<sha> (development channel)
|
||
# any other branch / PR → <tag>-dev+<sha> (no publish)
|
||
#
|
||
# The base version is derived from the most recent git tag (vX.Y.Z).
|
||
# No VERSION file needed — the tag IS the version.
|
||
- name: Resolve version and channel
|
||
id: vars
|
||
run: |
|
||
SHORT_SHA="${GITHUB_SHA:0:7}"
|
||
REF="${{ github.ref }}"
|
||
IS_RELEASE="false"
|
||
|
||
# Base version from our $VERSION in FreeNAS.pm — the single source of truth.
|
||
# Bump that variable when starting a new release series; git tags trigger publishing.
|
||
BASE_VERSION="$(perl -ne 'if (/our\s+\$VERSION\s*=\s*['"'"'"]([^'"'"'"]+)/) { print $1; exit }' \
|
||
perl5/PVE/Storage/LunCmd/FreeNAS.pm 2>/dev/null || echo '0.0.0')"
|
||
|
||
# Only an exact vX.Y.Z tag (no suffix) is a stable release.
|
||
if [[ "$REF" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||
VERSION="${REF#refs/tags/v}"
|
||
CHANNEL="stable"
|
||
CLOUDSMITH_REPO="truenas-proxmox"
|
||
IS_RELEASE="true"
|
||
if [[ "$VERSION" != "$BASE_VERSION" ]]; then
|
||
echo "::warning::Tag version ($VERSION) does not match \$VERSION in FreeNAS.pm ($BASE_VERSION)"
|
||
fi
|
||
|
||
elif [[ "$REF" == refs/heads/master ]]; then
|
||
VERSION="${BASE_VERSION}-beta+${SHORT_SHA}"
|
||
CHANNEL="testing"
|
||
CLOUDSMITH_REPO="truenas-proxmox-testing"
|
||
|
||
elif [[ "$REF" == refs/heads/feature_* ]]; then
|
||
VERSION="${BASE_VERSION}-alpha+${SHORT_SHA}"
|
||
CHANNEL="development"
|
||
CLOUDSMITH_REPO="truenas-proxmox-snapshots"
|
||
|
||
else
|
||
VERSION="${BASE_VERSION}-dev+${SHORT_SHA}"
|
||
CHANNEL="none"
|
||
CLOUDSMITH_REPO=""
|
||
fi
|
||
|
||
DEB_FILE="${PACKAGE_NAME}_${VERSION}_all.deb"
|
||
|
||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||
echo "deb_file=${DEB_FILE}" >> "$GITHUB_OUTPUT"
|
||
echo "channel=${CHANNEL}" >> "$GITHUB_OUTPUT"
|
||
echo "cloudsmith_repo=${CLOUDSMITH_REPO}" >> "$GITHUB_OUTPUT"
|
||
echo "is_release=${IS_RELEASE}" >> "$GITHUB_OUTPUT"
|
||
|
||
{
|
||
echo "### Build Summary"
|
||
echo "| | |"
|
||
echo "|---|---|"
|
||
echo "| Version | \`${VERSION}\` |"
|
||
echo "| Channel | \`${CHANNEL}\` |"
|
||
echo "| Package | \`${DEB_FILE}\` |"
|
||
} >> "$GITHUB_STEP_SUMMARY"
|
||
|
||
# ── Assemble staging directory ─────────────────────────────────────────
|
||
- name: Assemble package staging directory
|
||
run: |
|
||
VERSION="${{ steps.vars.outputs.version }}"
|
||
STAGING="dist"
|
||
|
||
mkdir -p "${STAGING}/DEBIAN"
|
||
mkdir -p "${STAGING}/usr/share/freenas-proxmox/patches/ZFSPlugin"
|
||
mkdir -p "${STAGING}/usr/share/freenas-proxmox/patches/pvemanagerlib"
|
||
mkdir -p "${STAGING}/usr/share/freenas-proxmox/patches/apidoc"
|
||
|
||
# Generate control file from template
|
||
sed "s/\${VERSION}/${VERSION}/" packaging/DEBIAN/control.j2 \
|
||
> "${STAGING}/DEBIAN/control"
|
||
|
||
# Maintainer scripts
|
||
cp packaging/DEBIAN/postinst "${STAGING}/DEBIAN/postinst"
|
||
cp packaging/DEBIAN/postrm "${STAGING}/DEBIAN/postrm"
|
||
cp packaging/DEBIAN/triggers "${STAGING}/DEBIAN/triggers"
|
||
chmod 0755 "${STAGING}/DEBIAN/postinst" "${STAGING}/DEBIAN/postrm"
|
||
|
||
# Plugin source files
|
||
cp perl5/PVE/Storage/LunCmd/FreeNAS.pm "${STAGING}/usr/share/freenas-proxmox/FreeNAS.pm"
|
||
cp perl5/REST/Client.pm "${STAGING}/usr/share/freenas-proxmox/REST-Client.pm"
|
||
|
||
# PVE 8 patches (8.0.x – 8.3.x)
|
||
cp stable-8/perl5/PVE/Storage/ZFSPlugin.pm.patch \
|
||
"${STAGING}/usr/share/freenas-proxmox/patches/ZFSPlugin/8.patch"
|
||
|
||
# PVE 8.4.x ZFSPlugin patch — indentation changed in 8.4, incompatible with 8.patch
|
||
cp stable-8/perl5/PVE/Storage/ZFSPlugin-8.4.14_1.pm.patch \
|
||
"${STAGING}/usr/share/freenas-proxmox/patches/ZFSPlugin/8.4.patch"
|
||
echo "Bundled PVE 8.4.x ZFSPlugin patch"
|
||
cp stable-8/pve-manager/js/pvemanagerlib.js.patch \
|
||
"${STAGING}/usr/share/freenas-proxmox/patches/pvemanagerlib/8.patch"
|
||
cp stable-8/pve-docs/api-viewer/apidoc.js.patch \
|
||
"${STAGING}/usr/share/freenas-proxmox/patches/apidoc/8.patch"
|
||
|
||
# PVE 8.4.x pvemanagerlib patch — JS was reformatted in 8.4, incompatible with 8.patch
|
||
cp stable-8/pve-manager/js/pvemanagerlib-8.4.14_1.js.patch \
|
||
"${STAGING}/usr/share/freenas-proxmox/patches/pvemanagerlib/8.4.patch"
|
||
echo "Bundled PVE 8.4.x pvemanagerlib patch"
|
||
|
||
# PVE 7 patches (best-effort — use latest versioned patch available)
|
||
for type in ZFSPlugin pvemanagerlib apidoc; do
|
||
case "$type" in
|
||
ZFSPlugin) glob="stable-7/perl5/PVE/Storage/ZFSPlugin-*.pm.patch" ;;
|
||
pvemanagerlib) glob="stable-7/pve-manager/js/pvemanagerlib-*.js.patch" ;;
|
||
apidoc) glob="stable-7/pve-docs/api-viewer/apidoc-*.js.patch" ;;
|
||
esac
|
||
latest=$(ls $glob 2>/dev/null | sort -V | tail -1 || true)
|
||
if [ -n "$latest" ]; then
|
||
cp "$latest" "${STAGING}/usr/share/freenas-proxmox/patches/${type}/7.patch"
|
||
echo "Bundled PVE-7 ${type} patch: $(basename $latest)"
|
||
else
|
||
echo "No PVE-7 ${type} patch found — skipping"
|
||
fi
|
||
done
|
||
|
||
echo "==> Package contents:"
|
||
find "${STAGING}" | sort
|
||
|
||
- name: Build .deb
|
||
run: |
|
||
sudo dpkg-deb -Zgzip --build dist "${{ steps.vars.outputs.deb_file }}"
|
||
|
||
- name: Verify .deb
|
||
run: |
|
||
echo "==> Package info:"
|
||
dpkg-deb --info "${{ steps.vars.outputs.deb_file }}"
|
||
echo ""
|
||
echo "==> Package contents:"
|
||
dpkg-deb --contents "${{ steps.vars.outputs.deb_file }}"
|
||
|
||
- name: Upload package artifact
|
||
uses: actions/upload-artifact@v7
|
||
with:
|
||
name: ${{ steps.vars.outputs.deb_file }}
|
||
path: ${{ steps.vars.outputs.deb_file }}
|
||
retention-days: 30
|
||
|
||
# ── Job 4: Security scan ─────────────────────────────────────────────────────
|
||
security:
|
||
name: Security Scan
|
||
runs-on: ubuntu-latest
|
||
needs: build
|
||
|
||
steps:
|
||
- uses: actions/checkout@v6
|
||
|
||
# Scan the repository for secrets and known vulnerabilities
|
||
- name: Run Trivy (repo scan — secrets + misconfig)
|
||
uses: aquasecurity/trivy-action@master
|
||
with:
|
||
scan-type: fs
|
||
scan-ref: .
|
||
scanners: secret,misconfig
|
||
severity: HIGH,CRITICAL
|
||
exit-code: 1
|
||
format: table
|
||
|
||
# Download and scan the built .deb
|
||
- name: Download built package
|
||
uses: actions/download-artifact@v8
|
||
with:
|
||
name: ${{ needs.build.outputs.deb_file }}
|
||
|
||
- name: Extract and scan .deb contents
|
||
run: |
|
||
mkdir -p deb-contents
|
||
dpkg-deb --extract "${{ needs.build.outputs.deb_file }}" deb-contents/
|
||
|
||
- name: Run Trivy (package contents — vuln + secret)
|
||
uses: aquasecurity/trivy-action@master
|
||
with:
|
||
scan-type: fs
|
||
scan-ref: deb-contents
|
||
scanners: vuln,secret
|
||
severity: HIGH,CRITICAL
|
||
exit-code: 1
|
||
format: table
|
||
|
||
# ── Job 5: Publish ───────────────────────────────────────────────────────────
|
||
publish:
|
||
name: Publish
|
||
runs-on: ubuntu-latest
|
||
needs: [build, security]
|
||
# Only publish on direct pushes (not PRs) to tracked branches or tags
|
||
if: github.event_name == 'push' && needs.build.outputs.channel != 'none'
|
||
|
||
steps:
|
||
- uses: actions/checkout@v6
|
||
|
||
- name: Download built package
|
||
uses: actions/download-artifact@v8
|
||
with:
|
||
name: ${{ needs.build.outputs.deb_file }}
|
||
|
||
- name: Publish to Cloudsmith
|
||
uses: cloudsmith-io/action@master
|
||
with:
|
||
api-key: ${{ secrets.CLOUDSMITH_API_KEY }}
|
||
command: push
|
||
format: deb
|
||
owner: ksatechnologies
|
||
repo: ${{ needs.build.outputs.cloudsmith_repo }}
|
||
distro: debian
|
||
release: any-version
|
||
file: ${{ needs.build.outputs.deb_file }}
|
||
|
||
- name: Create draft GitHub Release
|
||
if: needs.build.outputs.is_release == 'true'
|
||
uses: softprops/action-gh-release@v2
|
||
with:
|
||
name: "v${{ needs.build.outputs.version }}"
|
||
draft: true
|
||
files: ${{ needs.build.outputs.deb_file }}
|
||
generate_release_notes: false
|
||
body: |
|
||
## freenas-proxmox v${{ needs.build.outputs.version }}
|
||
|
||
> **Edit before publishing** — fill in tested versions and remove inapplicable notices below.
|
||
|
||
### ⚠️ Version Compatibility Notices
|
||
|
||
| Your Proxmox VE | What to do |
|
||
|:----------------|:-----------|
|
||
| **PVE 9+** | ❌ Do **not** install v2.x — use v3.0 when released |
|
||
| **PVE 8.x** | ✅ Supported. Note: PVE 8 EOL is **2026-08-31** — plan your upgrade to PVE 9 + v3.0 |
|
||
| **PVE 7.x** | ⚠️ Best-effort only. This is the **last v2.x release** supporting PVE 7. Do **not** upgrade to v3.0 — stay on v2.x |
|
||
| **PVE 6 or older** | ❌ Not supported |
|
||
|
||
### Tested Proxmox VE Versions
|
||
- Proxmox VE 8.4.x (tested: )
|
||
- Proxmox VE 8.3.x (tested: )
|
||
- Proxmox VE 7.x (best-effort: )
|
||
|
||
### Tested TrueNAS Versions
|
||
- TrueNAS CORE:
|
||
- TrueNAS SCALE:
|
||
|
||
### Installation
|
||
See [README → Installation](https://github.com/TheGrandWazoo/freenas-proxmox#installation).
|
||
|
||
### Changes
|
||
See [CHANGELOG.md](https://github.com/TheGrandWazoo/freenas-proxmox/blob/master/CHANGELOG.md#unreleased).
|