@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.
Files changed (80) hide show
  1. package/.github/workflows/ci.yml +42 -0
  2. package/.github/workflows/release-please.yml +88 -0
  3. package/.github/workflows/release.yml +87 -0
  4. package/.release-please-manifest.json +3 -0
  5. package/CHANGELOG.md +102 -0
  6. package/CLAUDE.md +35 -0
  7. package/dist/index.js +179 -0
  8. package/jest.config.js +23 -0
  9. package/package.json +27 -23
  10. package/pulumi-resource-homelab +3 -0
  11. package/pulumi-resource-homelab.cmd +2 -0
  12. package/release-please-config.json +22 -0
  13. package/schema.json +1109 -0
  14. package/{index.d.ts → sdk/nodejs/index.d.ts} +3 -1
  15. package/{lxcContainer.d.ts → sdk/nodejs/lxcContainer.d.ts} +12 -6
  16. package/sdk/nodejs/notificationSettings.d.ts +46 -0
  17. package/sdk/nodejs/package-lock.json +2967 -0
  18. package/sdk/nodejs/package.json +35 -0
  19. package/{stack.d.ts → sdk/nodejs/stack.d.ts} +9 -0
  20. package/sdk/nodejs/unraidVm.d.ts +32 -0
  21. package/src/composeDiff.ts +197 -0
  22. package/src/envDiff.ts +41 -0
  23. package/src/helpers.ts +114 -0
  24. package/src/homelabClient.ts +316 -0
  25. package/src/index.ts +218 -0
  26. package/src/opnsenseClient.ts +553 -0
  27. package/src/provider.ts +100 -0
  28. package/src/resources/lxcContainer.ts +285 -0
  29. package/src/resources/notificationSettings.ts +157 -0
  30. package/src/resources/opnsenseAlias.ts +176 -0
  31. package/src/resources/opnsenseFirewallRule.ts +194 -0
  32. package/src/resources/opnsenseUnboundAcl.ts +182 -0
  33. package/src/resources/opnsenseUnboundDnsbl.ts +174 -0
  34. package/src/resources/opnsenseUnboundForward.ts +182 -0
  35. package/src/resources/opnsenseUnboundHostOverride.ts +180 -0
  36. package/src/resources/stack.ts +326 -0
  37. package/src/resources/traefikRoute.ts +199 -0
  38. package/src/resources/traefikStaticConfig.ts +157 -0
  39. package/src/resources/unraidVm.ts +217 -0
  40. package/tests/composeDiff.test.ts +256 -0
  41. package/tests/envDiff.test.ts +84 -0
  42. package/tests/helpers.test.ts +313 -0
  43. package/tests/homelabClient.test.ts +530 -0
  44. package/tests/lxcClient.test.ts +240 -0
  45. package/tests/lxcContainer.test.ts +504 -0
  46. package/tests/notificationSettings.test.ts +197 -0
  47. package/tests/opnsenseAlias.test.ts +383 -0
  48. package/tests/opnsenseClient.test.ts +1669 -0
  49. package/tests/opnsenseFirewallRule.test.ts +397 -0
  50. package/tests/opnsenseUnboundAcl.test.ts +316 -0
  51. package/tests/opnsenseUnboundDnsbl.test.ts +314 -0
  52. package/tests/opnsenseUnboundForward.test.ts +305 -0
  53. package/tests/opnsenseUnboundHostOverride.test.ts +320 -0
  54. package/tests/provider.test.ts +243 -0
  55. package/tests/stack.test.ts +675 -0
  56. package/tests/testUtils.ts +123 -0
  57. package/tests/traefikRoute.test.ts +565 -0
  58. package/tests/unraidVm.test.ts +272 -0
  59. package/tsconfig.json +18 -0
  60. package/index.js +0 -27
  61. package/lxcContainer.js +0 -55
  62. package/opnsenseAlias.js +0 -52
  63. package/opnsenseFirewallRule.js +0 -52
  64. package/opnsenseUnboundAcl.js +0 -52
  65. package/opnsenseUnboundDnsbl.js +0 -52
  66. package/opnsenseUnboundForward.js +0 -52
  67. package/opnsenseUnboundHostOverride.js +0 -52
  68. package/provider.js +0 -62
  69. package/stack.js +0 -73
  70. package/traefikRoute.js +0 -55
  71. package/traefikStaticConfig.js +0 -55
  72. /package/{opnsenseAlias.d.ts → sdk/nodejs/opnsenseAlias.d.ts} +0 -0
  73. /package/{opnsenseFirewallRule.d.ts → sdk/nodejs/opnsenseFirewallRule.d.ts} +0 -0
  74. /package/{opnsenseUnboundAcl.d.ts → sdk/nodejs/opnsenseUnboundAcl.d.ts} +0 -0
  75. /package/{opnsenseUnboundDnsbl.d.ts → sdk/nodejs/opnsenseUnboundDnsbl.d.ts} +0 -0
  76. /package/{opnsenseUnboundForward.d.ts → sdk/nodejs/opnsenseUnboundForward.d.ts} +0 -0
  77. /package/{opnsenseUnboundHostOverride.d.ts → sdk/nodejs/opnsenseUnboundHostOverride.d.ts} +0 -0
  78. /package/{provider.d.ts → sdk/nodejs/provider.d.ts} +0 -0
  79. /package/{traefikRoute.d.ts → sdk/nodejs/traefikRoute.d.ts} +0 -0
  80. /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 }}
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.3.5"
3
+ }
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.3.5",
4
- "description": "Pulumi SDK for managing homelab infrastructure",
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
- "@pulumi/pulumi": "^3.132.0"
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
- "typescript": "^5.0.0",
20
- "@types/node": "^20.0.0"
21
- },
22
- "repository": {
23
- "type": "git",
24
- "url": "https://github.com/tyevco/pulumi-homelab.git",
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,3 @@
1
+ #!/bin/bash
2
+ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
3
+ node "$DIR/dist/index.js"
@@ -0,0 +1,2 @@
1
+ @echo off
2
+ node "%~dp0dist\index.js" %*
@@ -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
+ }