GitHub is removing Node.js 20 from Actions runners on Sep 16, 2026
and forcing Node 24 as default from Jun 2, 2026. Upgrade all
first-party actions to latest major versions that ship with node24:
actions/checkout@v4 → v6
actions/upload-artifact@v4 → v7
actions/download-artifact@v4 → v8
Note: setup-python@v5 warning is inside cloudsmith-io/action@master
and is not directly addressable here.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RequireUseStrict and RequireUseWarnings require that strict and warnings
are the first statements after the package declaration. Move our \$VERSION
to after the two use pragmas.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
our \$VERSION = '2.3.0' is now the single source of truth for the release
series. The build.yml extracts it with a perl one-liner (no module load,
no stubs needed) and uses it as BASE_VERSION for alpha/beta channel builds.
Git tags remain the release trigger: pushing an exact vX.Y.Z tag publishes
to the stable Cloudsmith channel. A mismatch warning fires if the tag
version and \$VERSION disagree, catching forgotten version bumps.
The v2.3.0-pre anchor tag approach is superseded by this; the anchor tag
can stay in history as a breadcrumb but \$VERSION is now what drives builds.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two changes:
1. BASE_VERSION now strips pre-release suffixes from the nearest ancestor tag
(e.g. v2.3.0-pre → 2.3.0), so anchor tags like v2.3.0-pre make feature
branch builds say 2.3.0-alpha+<sha> without "pre" leaking into the version.
2. Stable release detection changed from glob (refs/tags/v*) to an exact
semver regex (^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$). Tags with a suffix
(v2.3.0-pre, v2.3.0-rc1) build and validate but do not publish to Cloudsmith
stable — they fall through to the 'none' channel.
This lets us place a v2.3.0-pre anchor tag to version the development series
correctly, then later push v2.3.0 as the actual stable release.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two problems in run_create_lu:
1. If freenas_iscsi_create_extent returned undef, the next line accessed
$extent->{'id'} unconditionally, causing a crash rather than a clean error.
2. If freenas_iscsi_create_target_to_extent failed after a successful extent
creation, the code just died with "Unable to create lun", leaving the
newly created extent orphaned on TrueNAS with no target association.
Subsequent delete_lu calls could not find or clean up this extent because
it was never in the LUN list, requiring manual TrueNAS cleanup.
Fix: check extent creation result before dereferencing; on target-to-extent
failure, call freenas_iscsi_remove_extent to roll back the extent before
dying. The rollback is best-effort (failure is logged but not re-thrown)
so a secondary API error does not mask the primary failure message.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All of these were module-level scalars that the last freenas_api_check call
would overwrite. With two TrueNAS hosts in a Proxmox config (e.g., one CORE
and one SCALE), the second host's API check would clobber the first host's
product_name, dev_prefix, and api_version, causing wrong API paths and wrong
pool-name slash-vs-dash conversion on subsequent calls to the first host.
Changes:
- $freenas_server_list->{$host} now stores a hashref {client, product_name,
api_version, dev_prefix, runaway_count} instead of a bare REST::Client
- freenas_api_connect: recursion counter is now per-host (runaway_count);
$apiping made local so v1->v2 detection for one host does not affect others
- freenas_api_check: fixed broken init-guard (was testing $freenas_rest_connection->{$host}
which always evaluates undef on a REST::Client object); saves detected
product_name/api_version/dev_prefix back into the per-host hash after detection
- freenas_api_call: restores all module-level pointers (product_name, dev_prefix,
api_methods, api_variables) from per-host storage on every call, so the
correct host context is active regardless of which host was accessed last
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
'! $method =~ /pattern/' is parsed as '(! $method) =~ /pattern/' due to
precedence, so the regex ran against "" and always returned false. The
guard never fired and any method string passed through to the API call.
Changed to '$method !~ /^(?:GET|DELETE|POST)$/' which correctly tests the
method string. Also replaced atomic group (?>...) with non-capturing (?:...)
since there is no backtracking concern here.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TrueNAS 25.04 removed the v1.0 REST API entirely. Previously it redirected
(302) to v2.0; now it returns 404. The plugin treated 404 as a fatal
connection failure, preventing any operation on 25.04 hosts.
Fix: extend the v1.0→v2.0 upgrade condition in freenas_api_connect to also
trigger on HTTP 404, but only while probing the v1.0 endpoint (guarded by
`$apiping =~ /v1\.0/`). A 404 on the v2.0 endpoint still fails cleanly.
Also add a syslog warn when Bearer Token auth is attempted over plain HTTP:
TrueNAS 25.04+ revokes API keys sent without SSL, causing auth failures that
are otherwise invisible in the plugin log. The warning points admins to the
"Use SSL" storage config option.
Closes#205
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
freenas_iscsi_create_extent and freenas_iscsi_create_target_to_extent
both used `eval $value` to expand template variable names like '$name'
into their runtime values. Replace with a local %vars hash and an
`exists` lookup — same behavior, no stringy eval, no perlcritic warning.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dpkg-deb --build reads a binary control file and rejects the two-stanza
source+binary format. Rewrote control.j2 as a single binary-only stanza.
Removed the source stanza (Source, Standards-Version, Build-Depends fields)
which are only meaningful in source packages built with dpkg-buildpackage.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
REST_CLIENT_PATH was defined but never referenced. REST/Client.pm is owned
by librest-client-perl and is intentionally not removed by our postrm
(documented in the comment above remove_plugin_files). Remove the dead variable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ProhibitExplicitReturnUndef (severity 5): returning undef explicitly is
wrong because it forces scalar context on callers. 'return;' returns
undef in scalar context and an empty list in list context, which is the
correct idiom. Fixed all 7 occurrences in FreeNAS.pm.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
perl -c requires all modules to be loadable at compile time. PVE::SafeSyslog
only exists on Proxmox VE hosts, so the syntax check always failed on
GitHub Actions runners. Fix:
- Install real Perl deps via apt (libwww-perl, libio-socket-ssl-perl,
librest-client-perl, libjson-perl)
- Create a minimal stub for PVE::SafeSyslog so perl -c can load FreeNAS.pm
- Pass -I/tmp/pve-stub to perl -c so the stub is found
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unquoted YAML plain scalars cannot contain ': ' (colon-space) because
the parser interprets it as a nested key-value mapping. The two step
names 'Dry-run patch: ZFSPlugin (PVE 8)' and 'Dry-run patch: apidoc.js
(PVE 8)' triggered this, causing GitHub Actions to reject the workflow
file before any jobs ran.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs that prevented any jobs from running:
1. action.yml contained only YAML comments (no 'on:' or 'jobs:' keys),
making it an invalid workflow file. GitHub rejected it at parse time.
Fixed by giving it a minimal valid structure with workflow_dispatch
trigger only, so it never fires automatically.
2. build.yml perlcritic step referenced perl5/PVE/Storage/Custom/FreeNAS.pm
which is not committed to the repository, causing a step failure.
Removed — only FreeNAS.pm (LunCmd) is linted for now.
3. build.yml publish job used 'if: |' block scalar for a multi-line
expression, which GitHub Actions rejects in expression position.
Changed to a single inline expression string.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update README.md to be more structured and selective from a visitors view.
- Update the Roadmap section.
- Update the Donators list.
- Update the New Install Instructions section to allow the visitor to select the GPG location they want to use.
- Update the Activity section.
- Fix spelling errors.
- When SSL redirect is enabled on TrueNAS and a change from V1 to V2
api's needs to happen then we overrun the limit of 1 on the recursion
loop. Set it to 2.
- Added some logging for TrueNAS-Scale conversion from slash (/) to dash
(-).
- Updated new repo install.
- Updated Roadmap.
- Cleanup old information.
- Updated Donation list.
- Changed FreeNAS to TrueNAS in most of the instructions.
- When changing from slashes (/) to dashes (-), I did not provide the
global parameter so a Storage/Tank/Disks would turn out to be
Storage-Tank/Disks causing a iSCSI: Failed to connect to LUN :
iscsi_service failed with : iscsi_service_reconnect_if_loggedin. Can not
reconnect right now.
- Created conditional to parse new TrueNAS 13 version.
- Created conditional to check for a 200 and that the Content-Type is
text/html so it will change to v2.0 of the API's. This is new behavior
in TrueNAS-Core 13.