@tyevco/pulumi-homelab 0.3.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +42 -0
- package/.github/workflows/release-please.yml +88 -0
- package/.github/workflows/release.yml +87 -0
- package/.release-please-manifest.json +3 -0
- package/CHANGELOG.md +102 -0
- package/CLAUDE.md +35 -0
- package/dist/index.js +179 -0
- package/jest.config.js +23 -0
- package/package.json +27 -23
- package/pulumi-resource-homelab +3 -0
- package/pulumi-resource-homelab.cmd +2 -0
- package/release-please-config.json +22 -0
- package/schema.json +1109 -0
- package/{index.d.ts → sdk/nodejs/index.d.ts} +3 -1
- package/{lxcContainer.d.ts → sdk/nodejs/lxcContainer.d.ts} +12 -6
- package/sdk/nodejs/notificationSettings.d.ts +46 -0
- package/sdk/nodejs/package-lock.json +2967 -0
- package/sdk/nodejs/package.json +35 -0
- package/{stack.d.ts → sdk/nodejs/stack.d.ts} +9 -0
- package/sdk/nodejs/unraidVm.d.ts +32 -0
- package/src/composeDiff.ts +197 -0
- package/src/envDiff.ts +41 -0
- package/src/helpers.ts +114 -0
- package/src/homelabClient.ts +316 -0
- package/src/index.ts +218 -0
- package/src/opnsenseClient.ts +553 -0
- package/src/provider.ts +100 -0
- package/src/resources/lxcContainer.ts +285 -0
- package/src/resources/notificationSettings.ts +157 -0
- package/src/resources/opnsenseAlias.ts +176 -0
- package/src/resources/opnsenseFirewallRule.ts +194 -0
- package/src/resources/opnsenseUnboundAcl.ts +182 -0
- package/src/resources/opnsenseUnboundDnsbl.ts +174 -0
- package/src/resources/opnsenseUnboundForward.ts +182 -0
- package/src/resources/opnsenseUnboundHostOverride.ts +180 -0
- package/src/resources/stack.ts +326 -0
- package/src/resources/traefikRoute.ts +199 -0
- package/src/resources/traefikStaticConfig.ts +157 -0
- package/src/resources/unraidVm.ts +217 -0
- package/tests/composeDiff.test.ts +256 -0
- package/tests/envDiff.test.ts +84 -0
- package/tests/helpers.test.ts +313 -0
- package/tests/homelabClient.test.ts +530 -0
- package/tests/lxcClient.test.ts +240 -0
- package/tests/lxcContainer.test.ts +504 -0
- package/tests/notificationSettings.test.ts +197 -0
- package/tests/opnsenseAlias.test.ts +383 -0
- package/tests/opnsenseClient.test.ts +1669 -0
- package/tests/opnsenseFirewallRule.test.ts +397 -0
- package/tests/opnsenseUnboundAcl.test.ts +316 -0
- package/tests/opnsenseUnboundDnsbl.test.ts +314 -0
- package/tests/opnsenseUnboundForward.test.ts +305 -0
- package/tests/opnsenseUnboundHostOverride.test.ts +320 -0
- package/tests/provider.test.ts +243 -0
- package/tests/stack.test.ts +675 -0
- package/tests/testUtils.ts +123 -0
- package/tests/traefikRoute.test.ts +565 -0
- package/tests/unraidVm.test.ts +272 -0
- package/tsconfig.json +18 -0
- package/index.js +0 -27
- package/lxcContainer.js +0 -55
- package/opnsenseAlias.js +0 -52
- package/opnsenseFirewallRule.js +0 -52
- package/opnsenseUnboundAcl.js +0 -52
- package/opnsenseUnboundDnsbl.js +0 -52
- package/opnsenseUnboundForward.js +0 -52
- package/opnsenseUnboundHostOverride.js +0 -52
- package/provider.js +0 -62
- package/stack.js +0 -73
- package/traefikRoute.js +0 -55
- package/traefikStaticConfig.js +0 -55
- /package/{opnsenseAlias.d.ts → sdk/nodejs/opnsenseAlias.d.ts} +0 -0
- /package/{opnsenseFirewallRule.d.ts → sdk/nodejs/opnsenseFirewallRule.d.ts} +0 -0
- /package/{opnsenseUnboundAcl.d.ts → sdk/nodejs/opnsenseUnboundAcl.d.ts} +0 -0
- /package/{opnsenseUnboundDnsbl.d.ts → sdk/nodejs/opnsenseUnboundDnsbl.d.ts} +0 -0
- /package/{opnsenseUnboundForward.d.ts → sdk/nodejs/opnsenseUnboundForward.d.ts} +0 -0
- /package/{opnsenseUnboundHostOverride.d.ts → sdk/nodejs/opnsenseUnboundHostOverride.d.ts} +0 -0
- /package/{provider.d.ts → sdk/nodejs/provider.d.ts} +0 -0
- /package/{traefikRoute.d.ts → sdk/nodejs/traefikRoute.d.ts} +0 -0
- /package/{traefikStaticConfig.d.ts → sdk/nodejs/traefikStaticConfig.d.ts} +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
name: Build & Validate
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Setup Node.js
|
|
19
|
+
uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: 20
|
|
22
|
+
cache: npm
|
|
23
|
+
|
|
24
|
+
- name: Install provider dependencies
|
|
25
|
+
run: npm ci
|
|
26
|
+
|
|
27
|
+
- name: Build provider
|
|
28
|
+
run: npm run build
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: npm test
|
|
32
|
+
|
|
33
|
+
- name: Validate schema
|
|
34
|
+
run: node -e "JSON.parse(require('fs').readFileSync('schema.json','utf8'))"
|
|
35
|
+
|
|
36
|
+
- name: Install SDK dependencies
|
|
37
|
+
working-directory: sdk/nodejs
|
|
38
|
+
run: npm ci
|
|
39
|
+
|
|
40
|
+
- name: Build SDK
|
|
41
|
+
working-directory: sdk/nodejs
|
|
42
|
+
run: npm run build
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
name: Release Please
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
pull-requests: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release-please:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
outputs:
|
|
16
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
17
|
+
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
18
|
+
steps:
|
|
19
|
+
- uses: googleapis/release-please-action@v4
|
|
20
|
+
id: release
|
|
21
|
+
with:
|
|
22
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
23
|
+
|
|
24
|
+
publish:
|
|
25
|
+
name: Build & Publish
|
|
26
|
+
needs: release-please
|
|
27
|
+
if: ${{ needs.release-please.outputs.release_created }}
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
|
|
30
|
+
steps:
|
|
31
|
+
- name: Checkout
|
|
32
|
+
uses: actions/checkout@v4
|
|
33
|
+
with:
|
|
34
|
+
ref: ${{ needs.release-please.outputs.tag_name }}
|
|
35
|
+
|
|
36
|
+
- name: Setup Node.js
|
|
37
|
+
uses: actions/setup-node@v4
|
|
38
|
+
with:
|
|
39
|
+
node-version: 20
|
|
40
|
+
cache: npm
|
|
41
|
+
registry-url: https://registry.npmjs.org
|
|
42
|
+
|
|
43
|
+
- name: Install dependencies
|
|
44
|
+
run: npm ci
|
|
45
|
+
|
|
46
|
+
- name: Build provider
|
|
47
|
+
run: npm run build
|
|
48
|
+
|
|
49
|
+
- name: Stage plugin archive
|
|
50
|
+
run: |
|
|
51
|
+
STAGING="$(mktemp -d)"
|
|
52
|
+
# Copy provider artifacts
|
|
53
|
+
cp -r dist "$STAGING/"
|
|
54
|
+
cp package.json schema.json "$STAGING/"
|
|
55
|
+
cp pulumi-resource-homelab pulumi-resource-homelab.cmd "$STAGING/"
|
|
56
|
+
# Install production-only dependencies into staging
|
|
57
|
+
cp package-lock.json "$STAGING/"
|
|
58
|
+
cd "$STAGING" && npm ci --omit=dev && rm package-lock.json
|
|
59
|
+
# Remove all .bin symlinks — Pulumi's archive extractor can't handle them
|
|
60
|
+
find node_modules -name '.bin' -type d -exec rm -rf {} +
|
|
61
|
+
echo "STAGING=$STAGING" >> "$GITHUB_ENV"
|
|
62
|
+
|
|
63
|
+
- name: Create platform archives
|
|
64
|
+
run: |
|
|
65
|
+
VERSION="${{ needs.release-please.outputs.tag_name }}"
|
|
66
|
+
VERSION="${VERSION#v}"
|
|
67
|
+
ARCHIVES="$(mktemp -d)"
|
|
68
|
+
cd "$STAGING"
|
|
69
|
+
for PLATFORM in linux-amd64 darwin-amd64 darwin-arm64 windows-amd64; do
|
|
70
|
+
ARCHIVE_NAME="pulumi-resource-homelab-v${VERSION}-${PLATFORM}.tar.gz"
|
|
71
|
+
tar czf "${ARCHIVES}/${ARCHIVE_NAME}" .
|
|
72
|
+
done
|
|
73
|
+
echo "ARCHIVES=$ARCHIVES" >> "$GITHUB_ENV"
|
|
74
|
+
|
|
75
|
+
- name: Upload release assets
|
|
76
|
+
env:
|
|
77
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
78
|
+
run: gh release upload "${{ needs.release-please.outputs.tag_name }}" ${{ env.ARCHIVES }}/*.tar.gz
|
|
79
|
+
|
|
80
|
+
- name: Build SDK
|
|
81
|
+
working-directory: sdk/nodejs
|
|
82
|
+
run: npm ci && npm run build
|
|
83
|
+
|
|
84
|
+
- name: Publish SDK to npm
|
|
85
|
+
working-directory: sdk/nodejs
|
|
86
|
+
run: npm publish --access public
|
|
87
|
+
env:
|
|
88
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Release (Manual)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release:
|
|
14
|
+
name: Build & Release
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Setup Node.js
|
|
22
|
+
uses: actions/setup-node@v4
|
|
23
|
+
with:
|
|
24
|
+
node-version: 20
|
|
25
|
+
cache: npm
|
|
26
|
+
registry-url: https://registry.npmjs.org
|
|
27
|
+
|
|
28
|
+
- name: Extract version from tag
|
|
29
|
+
id: version
|
|
30
|
+
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
|
31
|
+
|
|
32
|
+
- name: Patch version in package files
|
|
33
|
+
run: |
|
|
34
|
+
VERSION="${{ steps.version.outputs.VERSION }}"
|
|
35
|
+
# Update provider package.json
|
|
36
|
+
jq --arg v "$VERSION" '.version = $v' package.json > package.json.tmp && mv package.json.tmp package.json
|
|
37
|
+
# Update schema.json
|
|
38
|
+
jq --arg v "$VERSION" '.version = $v' schema.json > schema.json.tmp && mv schema.json.tmp schema.json
|
|
39
|
+
# Update SDK package.json
|
|
40
|
+
jq --arg v "$VERSION" '.version = $v' sdk/nodejs/package.json > sdk/nodejs/package.json.tmp && mv sdk/nodejs/package.json.tmp sdk/nodejs/package.json
|
|
41
|
+
|
|
42
|
+
- name: Install dependencies
|
|
43
|
+
run: npm ci
|
|
44
|
+
|
|
45
|
+
- name: Build provider
|
|
46
|
+
run: npm run build
|
|
47
|
+
|
|
48
|
+
- name: Stage plugin archive
|
|
49
|
+
run: |
|
|
50
|
+
STAGING="$(mktemp -d)"
|
|
51
|
+
# Copy provider artifacts
|
|
52
|
+
cp -r dist "$STAGING/"
|
|
53
|
+
cp package.json schema.json "$STAGING/"
|
|
54
|
+
cp pulumi-resource-homelab pulumi-resource-homelab.cmd "$STAGING/"
|
|
55
|
+
# Install production-only dependencies into staging
|
|
56
|
+
cp package-lock.json "$STAGING/"
|
|
57
|
+
cd "$STAGING" && npm ci --omit=dev && rm package-lock.json
|
|
58
|
+
# Remove all .bin symlinks — Pulumi's archive extractor can't handle them
|
|
59
|
+
find node_modules -name '.bin' -type d -exec rm -rf {} +
|
|
60
|
+
echo "STAGING=$STAGING" >> "$GITHUB_ENV"
|
|
61
|
+
|
|
62
|
+
- name: Create platform archives
|
|
63
|
+
run: |
|
|
64
|
+
VERSION="${{ steps.version.outputs.VERSION }}"
|
|
65
|
+
ARCHIVES="$(mktemp -d)"
|
|
66
|
+
cd "$STAGING"
|
|
67
|
+
for PLATFORM in linux-amd64 darwin-amd64 darwin-arm64 windows-amd64; do
|
|
68
|
+
ARCHIVE_NAME="pulumi-resource-homelab-v${VERSION}-${PLATFORM}.tar.gz"
|
|
69
|
+
tar czf "${ARCHIVES}/${ARCHIVE_NAME}" .
|
|
70
|
+
done
|
|
71
|
+
echo "ARCHIVES=$ARCHIVES" >> "$GITHUB_ENV"
|
|
72
|
+
|
|
73
|
+
- name: Create GitHub Release
|
|
74
|
+
uses: softprops/action-gh-release@v2
|
|
75
|
+
with:
|
|
76
|
+
generate_release_notes: true
|
|
77
|
+
files: ${{ env.ARCHIVES }}/*.tar.gz
|
|
78
|
+
|
|
79
|
+
- name: Build SDK
|
|
80
|
+
working-directory: sdk/nodejs
|
|
81
|
+
run: npm ci && npm run build
|
|
82
|
+
|
|
83
|
+
- name: Publish SDK to npm
|
|
84
|
+
working-directory: sdk/nodejs
|
|
85
|
+
run: npm publish --access public
|
|
86
|
+
env:
|
|
87
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.3.5](https://github.com/tyevco/pulumi-homelab/compare/v0.3.4...v0.3.5) (2026-03-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* omit Content-Type on GET requests and recreate stack on 404 ([c02fdfb](https://github.com/tyevco/pulumi-homelab/commit/c02fdfb76273cefd4563bb407e8995e4180a08ba))
|
|
9
|
+
* recreate stack on 404 during update ([eae8ca9](https://github.com/tyevco/pulumi-homelab/commit/eae8ca99f9505654ab3ee2f8be8f8d48b4d3b79b))
|
|
10
|
+
|
|
11
|
+
## [0.3.4](https://github.com/tyevco/pulumi-homelab/compare/v0.3.3...v0.3.4) (2026-03-01)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* omit Content-Type header on GET requests to OPNsense API ([79a7175](https://github.com/tyevco/pulumi-homelab/commit/79a7175853e9f5bb9bd28f28a6ffce2ec79d19ad))
|
|
17
|
+
* omit Content-Type on GET requests to OPNsense API ([0bf4d44](https://github.com/tyevco/pulumi-homelab/commit/0bf4d445e3bedf18bedd28f24eb6a94ef2632c0f))
|
|
18
|
+
|
|
19
|
+
## [0.3.3](https://github.com/tyevco/pulumi-homelab/compare/v0.3.2...v0.3.3) (2026-03-01)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* add not-found text detection to traefik static config read handler ([24e1222](https://github.com/tyevco/pulumi-homelab/commit/24e1222e4353a59f61b4cce3a3b5244c99f55607))
|
|
25
|
+
* add TXT to valid rr types in host override validation ([5526f3b](https://github.com/tyevco/pulumi-homelab/commit/5526f3befd237428265826057fc739b93ce74b33))
|
|
26
|
+
* consistent not-found detection in all read and delete handlers ([b9e9be9](https://github.com/tyevco/pulumi-homelab/commit/b9e9be9dc81b3264bdaa0d7dade7b6d831257b48))
|
|
27
|
+
* consistent not-found handling and test coverage improvements ([398d1af](https://github.com/tyevco/pulumi-homelab/commit/398d1af4a22ecea71cd7aabe8422b90c4ba06aa9))
|
|
28
|
+
* consistent not-found handling, stackToOutputs bug, and test coverage ([f26c586](https://github.com/tyevco/pulumi-homelab/commit/f26c586ddc8e264ac74fdefe11a390483447c01b))
|
|
29
|
+
* harden OPNsense getItem response parsing ([81b1b1c](https://github.com/tyevco/pulumi-homelab/commit/81b1b1c801b9af26326b48871de7572deee9304b))
|
|
30
|
+
* harden OPNsense getItem response parsing and Struct serialization ([9a93e3c](https://github.com/tyevco/pulumi-homelab/commit/9a93e3c2e8adc0cf53b4bff5df5f074c38a899d6))
|
|
31
|
+
* make OPNsense delete handlers match read handlers for not-found detection ([52c60e9](https://github.com/tyevco/pulumi-homelab/commit/52c60e98f789a53d9ad482b5005f4bf657b89b78))
|
|
32
|
+
* use strict undefined checks in stackToOutputs for envFile and composeYaml ([7533718](https://github.com/tyevco/pulumi-homelab/commit/7533718227f750fa734933715f108a46510e0fbb))
|
|
33
|
+
|
|
34
|
+
## [0.3.2](https://github.com/tyevco/pulumi-homelab/compare/v0.3.1...v0.3.2) (2026-03-01)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Bug Fixes
|
|
38
|
+
|
|
39
|
+
* add enum validation to check handlers ([413ff1b](https://github.com/tyevco/pulumi-homelab/commit/413ff1bf7a45848a45c58e2c46a654321eeeff50))
|
|
40
|
+
* code analysis round 4 - enum validation, autostart fix, test coverage ([d51ab45](https://github.com/tyevco/pulumi-homelab/commit/d51ab452d64a2680df957f7c81886005610b6570))
|
|
41
|
+
* move lxcContainer autostart to REPLACE_FIELDS ([7e51e66](https://github.com/tyevco/pulumi-homelab/commit/7e51e66bede034ac2e91927a38bbd62f54e94804))
|
|
42
|
+
|
|
43
|
+
## [0.3.1](https://github.com/tyevco/pulumi-homelab/compare/v0.3.0...v0.3.1) (2026-03-01)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Bug Fixes
|
|
47
|
+
|
|
48
|
+
* normalize OPNsense getItem response format for read/import ([9f2f180](https://github.com/tyevco/pulumi-homelab/commit/9f2f18084d9c816fa27bb0cd74cccfa397200706))
|
|
49
|
+
* OPNsense API normalization, diff stability, and edge cases ([fbfb0c4](https://github.com/tyevco/pulumi-homelab/commit/fbfb0c4c40e8cc8bd840d35e3eced9013e972a86))
|
|
50
|
+
* prevent spurious diffs between undefined and empty string ([155b14b](https://github.com/tyevco/pulumi-homelab/commit/155b14b754910bdd27d4206f5110d04c49ac4d4b))
|
|
51
|
+
* use nullish coalescing for opnsenseInsecure config ([6a15806](https://github.com/tyevco/pulumi-homelab/commit/6a158063a9c88a368f599aab62e262129b7cf765))
|
|
52
|
+
* use safeParseInt to prevent NaN from empty/invalid numeric strings ([e6bcc78](https://github.com/tyevco/pulumi-homelab/commit/e6bcc78715e73728a7e9b5f03efb3613037ba931))
|
|
53
|
+
|
|
54
|
+
## [0.3.0](https://github.com/tyevco/pulumi-homelab/compare/v0.2.3...v0.3.0) (2026-03-01)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### ⚠ BREAKING CHANGES
|
|
58
|
+
|
|
59
|
+
* Remove all dockge backward-compat aliases and rename client module. DockgeStack, DockgeStackArgs, DockgeContainerInfo, DockgeClientConfig, DockgeStackInfo, and configureDockgeClient exports are removed. The dockge:index:DockgeStack resource type is no longer supported. Config keys dockge:config:url and dockge:config:apiKey are removed. Use homelab:config:url and homelab:config:apiKey instead.
|
|
60
|
+
|
|
61
|
+
### Features
|
|
62
|
+
|
|
63
|
+
* add LXC container resource ([2cdb2f4](https://github.com/tyevco/pulumi-homelab/commit/2cdb2f495c9ae9b72bd1afd4cde3acefeb8eb71a))
|
|
64
|
+
* add mac as supported alias type for OpnsenseAlias ([7e87616](https://github.com/tyevco/pulumi-homelab/commit/7e87616958bb6f385d3c2c584499ddeff3af66a9))
|
|
65
|
+
* add mac as supported alias type for OpnsenseAlias ([2ebbbde](https://github.com/tyevco/pulumi-homelab/commit/2ebbbde093eb9e1d81dddc7b8c64f679a85546ea))
|
|
66
|
+
* remove dockge backward compatibility and rename to homelab ([1b2c921](https://github.com/tyevco/pulumi-homelab/commit/1b2c921261742350d799470678e871be0122f9c4))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
### Bug Fixes
|
|
70
|
+
|
|
71
|
+
* add docker-compose keyword to schema ([da87af7](https://github.com/tyevco/pulumi-homelab/commit/da87af7bc4338baa8056688a49d2e58eb4da8d78))
|
|
72
|
+
* add json.message support to homelabClient error extraction ([2a1b58c](https://github.com/tyevco/pulumi-homelab/commit/2a1b58c42c793a46c2ac0ffc0bd5b1e1f049d0b4))
|
|
73
|
+
* code analysis bug fixes and edge case tests ([5731b29](https://github.com/tyevco/pulumi-homelab/commit/5731b29a4f06e544d4e25751f7abcf863ac25fb7))
|
|
74
|
+
* code analysis round 2 - error handling and test coverage ([c055400](https://github.com/tyevco/pulumi-homelab/commit/c05540077fefdc05dfa1732853398587efb8c729))
|
|
75
|
+
* stop LXC container before deleting to prevent deletion failures ([7699b9f](https://github.com/tyevco/pulumi-homelab/commit/7699b9ff9f6b68d55ea0b1ec21817f4606d41ab9))
|
|
76
|
+
* unwrap secrets inside arrays recursively ([fd5267a](https://github.com/tyevco/pulumi-homelab/commit/fd5267aecc21ca99fdbd0e7fc0dec0dab3be8ba1))
|
|
77
|
+
|
|
78
|
+
## [0.2.3](https://github.com/tyevco/pulumi-homelab/compare/v0.2.2...v0.2.3) (2026-03-01)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
### Bug Fixes
|
|
82
|
+
|
|
83
|
+
* use gh release upload instead of softprops/action-gh-release ([7826a40](https://github.com/tyevco/pulumi-homelab/commit/7826a40609f177d2616a60ee18b00db3c6425a42))
|
|
84
|
+
|
|
85
|
+
## [0.2.2](https://github.com/tyevco/pulumi-homelab/compare/v0.2.1...v0.2.2) (2026-02-28)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
### Bug Fixes
|
|
89
|
+
|
|
90
|
+
* release-please tag matching, CI tests, and publish workflow ([7ee9ef5](https://github.com/tyevco/pulumi-homelab/commit/7ee9ef5846680a78fd08cdd15cfecba7011fd001))
|
|
91
|
+
* run publish steps directly in release-please workflow ([e10ec90](https://github.com/tyevco/pulumi-homelab/commit/e10ec9077dd36ea57e91a5cfe1b21bd67049ffe4))
|
|
92
|
+
|
|
93
|
+
## [0.2.1](https://github.com/tyevco/pulumi-homelab/compare/v0.2.0...v0.2.1) (2026-02-28)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
### Bug Fixes
|
|
97
|
+
|
|
98
|
+
* convert jest config from TypeScript to JavaScript ([7d2e68f](https://github.com/tyevco/pulumi-homelab/commit/7d2e68f1a281535f8e729ad7d4846f5779cca413))
|
|
99
|
+
* release-please tag matching and CI test runner ([c103565](https://github.com/tyevco/pulumi-homelab/commit/c103565c9c7faeb87ae28bf0b3c82412563f461b))
|
|
100
|
+
* resolve hardcoded version, unhandled promise rejections, and dead code ([2bef660](https://github.com/tyevco/pulumi-homelab/commit/2bef660c6c1c06824adc4ba77aaca68c0a5fd7f1))
|
|
101
|
+
* sync versions to v0.2.0, add tests to CI, remove dead code ([9192ae9](https://github.com/tyevco/pulumi-homelab/commit/9192ae9d2e43c1389ca5905b54c2ca7bfcc7efe2))
|
|
102
|
+
* use plain v* tags and sync package-lock version ([3f74b68](https://github.com/tyevco/pulumi-homelab/commit/3f74b687ccfa6d77df20af7f5bc5f853389226bd))
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Project Instructions
|
|
2
|
+
|
|
3
|
+
## Commit Message Format
|
|
4
|
+
|
|
5
|
+
This project uses [Conventional Commits](https://www.conventionalcommits.org/) to drive automated versioning via release-please.
|
|
6
|
+
|
|
7
|
+
Always use the following format for commit messages:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<type>[optional scope]: <description>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Types
|
|
14
|
+
|
|
15
|
+
- `feat:` — new feature (bumps minor version)
|
|
16
|
+
- `fix:` — bug fix (bumps patch version)
|
|
17
|
+
- `chore:` — maintenance tasks (no version bump)
|
|
18
|
+
- `docs:` — documentation only (no version bump)
|
|
19
|
+
- `test:` — adding or updating tests (no version bump)
|
|
20
|
+
- `refactor:` — code refactoring (no version bump)
|
|
21
|
+
- `ci:` — CI/CD changes (no version bump)
|
|
22
|
+
|
|
23
|
+
### Breaking Changes
|
|
24
|
+
|
|
25
|
+
- Use `feat!:` or `fix!:` for breaking changes (bumps major version, or minor while pre-1.0)
|
|
26
|
+
- Alternatively, add a `BREAKING CHANGE:` footer to any commit
|
|
27
|
+
|
|
28
|
+
### Examples
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
feat: add Traefik middleware resource
|
|
32
|
+
fix: handle empty compose file gracefully
|
|
33
|
+
feat!: rename Stack resource to DockgeStack
|
|
34
|
+
chore: update dev dependencies
|
|
35
|
+
```
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
39
|
+
const helpers_1 = require("./helpers");
|
|
40
|
+
const homelabClient_1 = require("./homelabClient");
|
|
41
|
+
const opnsenseClient_1 = require("./opnsenseClient");
|
|
42
|
+
const provider_1 = require("./provider");
|
|
43
|
+
const providerProto = require("@pulumi/pulumi/proto/provider_pb");
|
|
44
|
+
const providerGrpc = require("@pulumi/pulumi/proto/provider_grpc_pb");
|
|
45
|
+
const pluginProto = require("@pulumi/pulumi/proto/plugin_pb");
|
|
46
|
+
const emptyProto = require("google-protobuf/google/protobuf/empty_pb");
|
|
47
|
+
const schema = fs.readFileSync(path.join(__dirname, "..", "schema.json"), "utf-8");
|
|
48
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8"));
|
|
49
|
+
const providerImpl = {
|
|
50
|
+
getPluginInfo(call, callback) {
|
|
51
|
+
const response = new pluginProto.PluginInfo();
|
|
52
|
+
response.setVersion(packageJson.version);
|
|
53
|
+
callback(null, response);
|
|
54
|
+
},
|
|
55
|
+
getSchema(call, callback) {
|
|
56
|
+
const response = new providerProto.GetSchemaResponse();
|
|
57
|
+
response.setSchema(schema);
|
|
58
|
+
callback(null, response);
|
|
59
|
+
},
|
|
60
|
+
configure(call, callback) {
|
|
61
|
+
const args = (0, helpers_1.structToObject)(call.request.getArgs());
|
|
62
|
+
// Extract Homelab configuration (unwrap secrets)
|
|
63
|
+
const url = (0, helpers_1.unwrapSecret)(args["url"] || args["homelab:config:url"]) || "";
|
|
64
|
+
const apiKey = (0, helpers_1.unwrapSecret)(args["apiKey"] || args["homelab:config:apiKey"]) || "";
|
|
65
|
+
const endpoint = (0, helpers_1.unwrapSecret)(args["endpoint"] || args["homelab:config:endpoint"]) || undefined;
|
|
66
|
+
if (url && apiKey) {
|
|
67
|
+
(0, homelabClient_1.configureClient)({ url, apiKey, endpoint });
|
|
68
|
+
}
|
|
69
|
+
// Extract OPNsense configuration
|
|
70
|
+
const opnsenseUrl = (0, helpers_1.unwrapSecret)(args["opnsenseUrl"] || args["homelab:config:opnsenseUrl"]) || "";
|
|
71
|
+
const opnsenseApiKey = (0, helpers_1.unwrapSecret)(args["opnsenseApiKey"] || args["homelab:config:opnsenseApiKey"]) || "";
|
|
72
|
+
const opnsenseApiSecret = (0, helpers_1.unwrapSecret)(args["opnsenseApiSecret"] || args["homelab:config:opnsenseApiSecret"]) || "";
|
|
73
|
+
const opnsenseInsecure = (0, helpers_1.unwrapSecret)(args["opnsenseInsecure"] ?? args["homelab:config:opnsenseInsecure"]) ?? false;
|
|
74
|
+
if (opnsenseUrl && opnsenseApiKey && opnsenseApiSecret) {
|
|
75
|
+
(0, opnsenseClient_1.configureOpnsenseClient)({
|
|
76
|
+
url: opnsenseUrl,
|
|
77
|
+
apiKey: opnsenseApiKey,
|
|
78
|
+
apiSecret: opnsenseApiSecret,
|
|
79
|
+
insecure: opnsenseInsecure === true || opnsenseInsecure === "true",
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const response = new providerProto.ConfigureResponse();
|
|
83
|
+
response.setAcceptsecrets(true);
|
|
84
|
+
response.setSupportspreview(true);
|
|
85
|
+
callback(null, response);
|
|
86
|
+
},
|
|
87
|
+
checkConfig(call, callback) {
|
|
88
|
+
const news = (0, helpers_1.structToObject)(call.request.getNews());
|
|
89
|
+
const failures = [];
|
|
90
|
+
// Homelab cross-dependency: if either field is set, both are required
|
|
91
|
+
const url = news["url"] || "";
|
|
92
|
+
const apiKey = news["apiKey"] || "";
|
|
93
|
+
if (url && !apiKey) {
|
|
94
|
+
const failure = new providerProto.CheckFailure();
|
|
95
|
+
failure.setProperty("apiKey");
|
|
96
|
+
failure.setReason("apiKey is required when url is set. Set it via `pulumi config set --secret homelab:apiKey <key>`");
|
|
97
|
+
failures.push(failure);
|
|
98
|
+
}
|
|
99
|
+
if (!url && apiKey) {
|
|
100
|
+
const failure = new providerProto.CheckFailure();
|
|
101
|
+
failure.setProperty("url");
|
|
102
|
+
failure.setReason("url is required when apiKey is set. Set it via `pulumi config set homelab:url <url>`");
|
|
103
|
+
failures.push(failure);
|
|
104
|
+
}
|
|
105
|
+
// OPNsense cross-dependency: if any OPNsense field is set, all three required fields must be set
|
|
106
|
+
const opnsenseUrl = news["opnsenseUrl"] || "";
|
|
107
|
+
const opnsenseApiKey = news["opnsenseApiKey"] || "";
|
|
108
|
+
const opnsenseApiSecret = news["opnsenseApiSecret"] || "";
|
|
109
|
+
const anyOpnsense = opnsenseUrl || opnsenseApiKey || opnsenseApiSecret;
|
|
110
|
+
if (anyOpnsense) {
|
|
111
|
+
if (!opnsenseUrl) {
|
|
112
|
+
const failure = new providerProto.CheckFailure();
|
|
113
|
+
failure.setProperty("opnsenseUrl");
|
|
114
|
+
failure.setReason("opnsenseUrl is required when other OPNsense fields are set. Set it via `pulumi config set homelab:opnsenseUrl <url>`");
|
|
115
|
+
failures.push(failure);
|
|
116
|
+
}
|
|
117
|
+
if (!opnsenseApiKey) {
|
|
118
|
+
const failure = new providerProto.CheckFailure();
|
|
119
|
+
failure.setProperty("opnsenseApiKey");
|
|
120
|
+
failure.setReason("opnsenseApiKey is required when other OPNsense fields are set. Set it via `pulumi config set --secret homelab:opnsenseApiKey <key>`");
|
|
121
|
+
failures.push(failure);
|
|
122
|
+
}
|
|
123
|
+
if (!opnsenseApiSecret) {
|
|
124
|
+
const failure = new providerProto.CheckFailure();
|
|
125
|
+
failure.setProperty("opnsenseApiSecret");
|
|
126
|
+
failure.setReason("opnsenseApiSecret is required when other OPNsense fields are set. Set it via `pulumi config set --secret homelab:opnsenseApiSecret <secret>`");
|
|
127
|
+
failures.push(failure);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const response = new providerProto.CheckResponse();
|
|
131
|
+
response.setInputs(call.request.getNews());
|
|
132
|
+
failures.forEach((f) => response.addFailures(f));
|
|
133
|
+
callback(null, response);
|
|
134
|
+
},
|
|
135
|
+
diffConfig(call, callback) {
|
|
136
|
+
const response = new providerProto.DiffResponse();
|
|
137
|
+
callback(null, response);
|
|
138
|
+
},
|
|
139
|
+
check(call, callback) {
|
|
140
|
+
(0, provider_1.dispatchCheck)(call, callback);
|
|
141
|
+
},
|
|
142
|
+
diff(call, callback) {
|
|
143
|
+
(0, provider_1.dispatchDiff)(call, callback);
|
|
144
|
+
},
|
|
145
|
+
create(call, callback) {
|
|
146
|
+
(0, provider_1.dispatchCreate)(call, callback);
|
|
147
|
+
},
|
|
148
|
+
read(call, callback) {
|
|
149
|
+
(0, provider_1.dispatchRead)(call, callback);
|
|
150
|
+
},
|
|
151
|
+
update(call, callback) {
|
|
152
|
+
(0, provider_1.dispatchUpdate)(call, callback);
|
|
153
|
+
},
|
|
154
|
+
delete(call, callback) {
|
|
155
|
+
(0, provider_1.dispatchDelete)(call, callback);
|
|
156
|
+
},
|
|
157
|
+
invoke(call, callback) {
|
|
158
|
+
const tok = call.request.getTok();
|
|
159
|
+
callback({
|
|
160
|
+
code: grpc.status.UNIMPLEMENTED,
|
|
161
|
+
message: `Unknown function: ${tok}`,
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
cancel(call, callback) {
|
|
165
|
+
callback(null, new emptyProto.Empty());
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
// Start gRPC server
|
|
169
|
+
const server = new grpc.Server();
|
|
170
|
+
server.addService(providerGrpc.ResourceProviderService, providerImpl);
|
|
171
|
+
server.bindAsync("127.0.0.1:0", grpc.ServerCredentials.createInsecure(), (err, port) => {
|
|
172
|
+
if (err) {
|
|
173
|
+
console.error(`Failed to bind: ${err}`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
// Pulumi reads the port from stdout
|
|
177
|
+
console.log(port);
|
|
178
|
+
});
|
|
179
|
+
//# sourceMappingURL=index.js.map
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** @type {import('jest').Config} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
preset: "ts-jest",
|
|
4
|
+
testEnvironment: "node",
|
|
5
|
+
roots: ["<rootDir>/tests"],
|
|
6
|
+
transform: {
|
|
7
|
+
"^.+\\.tsx?$": [
|
|
8
|
+
"ts-jest",
|
|
9
|
+
{
|
|
10
|
+
tsconfig: {
|
|
11
|
+
rootDir: ".",
|
|
12
|
+
target: "ES2020",
|
|
13
|
+
module: "commonjs",
|
|
14
|
+
lib: ["ES2020"],
|
|
15
|
+
strict: true,
|
|
16
|
+
esModuleInterop: true,
|
|
17
|
+
skipLibCheck: true,
|
|
18
|
+
resolveJsonModule: true,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
};
|
package/package.json
CHANGED
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tyevco/pulumi-homelab",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Pulumi
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"typings": "index.d.ts",
|
|
7
|
-
"files": [
|
|
8
|
-
"*.js",
|
|
9
|
-
"*.d.ts",
|
|
10
|
-
"package.json"
|
|
11
|
-
],
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "A Pulumi native provider for managing homelab infrastructure via the homelab REST API",
|
|
5
|
+
"main": "dist/index.js",
|
|
12
6
|
"scripts": {
|
|
13
|
-
"build": "tsc"
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"clean": "rm -rf dist",
|
|
9
|
+
"rebuild": "npm run clean && npm run build",
|
|
10
|
+
"deploy:local": "npm run rebuild && VERSION=$(node -p \"require('./package.json').version\") && mkdir -p ~/.pulumi/plugins/resource-homelab-v${VERSION} && cp -r dist package.json node_modules schema.json pulumi-resource-homelab pulumi-resource-homelab.cmd ~/.pulumi/plugins/resource-homelab-v${VERSION}/",
|
|
11
|
+
"test": "jest"
|
|
14
12
|
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"pulumi",
|
|
15
|
+
"homelab",
|
|
16
|
+
"lxc",
|
|
17
|
+
"infrastructure",
|
|
18
|
+
"docker-compose"
|
|
19
|
+
],
|
|
20
|
+
"license": "Apache-2.0",
|
|
15
21
|
"dependencies": {
|
|
16
|
-
"@
|
|
22
|
+
"@grpc/grpc-js": "^1.8.0",
|
|
23
|
+
"@pulumi/pulumi": "^3.132.0",
|
|
24
|
+
"cross-fetch": "^4.0.0",
|
|
25
|
+
"google-protobuf": "3.21.4",
|
|
26
|
+
"yaml": "^2.8.2"
|
|
17
27
|
},
|
|
18
28
|
"devDependencies": {
|
|
19
|
-
"
|
|
20
|
-
"@types/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"directory": "sdk/nodejs"
|
|
26
|
-
},
|
|
27
|
-
"license": "Apache-2.0",
|
|
28
|
-
"pulumi": {
|
|
29
|
-
"resource": true,
|
|
30
|
-
"pluginDownloadURL": "github://api.github.com/tyevco/pulumi-homelab"
|
|
29
|
+
"@types/google-protobuf": "^3.15.0",
|
|
30
|
+
"@types/jest": "^30.0.0",
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"jest": "^30.2.0",
|
|
33
|
+
"ts-jest": "^29.4.6",
|
|
34
|
+
"typescript": "^5.0.0"
|
|
31
35
|
}
|
|
32
36
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"packages": {
|
|
3
|
+
".": {
|
|
4
|
+
"release-type": "node",
|
|
5
|
+
"bump-minor-pre-major": true,
|
|
6
|
+
"include-component-in-tag": false,
|
|
7
|
+
"extra-files": [
|
|
8
|
+
{
|
|
9
|
+
"type": "json",
|
|
10
|
+
"path": "schema.json",
|
|
11
|
+
"jsonpath": "$.version"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"type": "json",
|
|
15
|
+
"path": "sdk/nodejs/package.json",
|
|
16
|
+
"jsonpath": "$.version"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
|
|
22
|
+
}
|