permaweb-deploy 0.0.0-rc-20251001215907

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/README.md ADDED
@@ -0,0 +1,332 @@
1
+ # Permaweb Deploy
2
+
3
+ Inspired by the [cookbook github action deployment guide](https://cookbook.arweave.dev/guides/deployment/github-action.html), `permaweb-deploy` is a Node.js command-line tool designed to streamline the deployment of web applications to the permaweb using Arweave. It uploads your build folder or a single file, creates Arweave manifests, and updates ArNS (Arweave Name Service) records via ANT (Arweave Name Token) with the transaction ID.
4
+
5
+ ## Features
6
+
7
+ - **Turbo SDK Integration:** Uses Turbo SDK for fast, reliable file uploads to Arweave
8
+ - **Arweave Manifest v0.2.0:** Creates manifests with fallback support for SPAs
9
+ - **ArNS Updates:** Updates ArNS records via ANT with new transaction IDs and metadata
10
+ - **Automated Workflow:** Integrates with GitHub Actions for continuous deployment
11
+ - **Git Hash Tagging:** Automatically tags deployments with Git commit hashes
12
+ - **404 Fallback Detection:** Automatically detects and sets 404.html as fallback
13
+ - **Network Support:** Supports mainnet, testnet, and custom ARIO process IDs
14
+ - **Flexible Deployment:** Supports deploying a folder or a single file
15
+ - **Modern CLI:** Built with oclif for a robust command-line experience
16
+ - **TypeScript:** Fully typed for better developer experience
17
+
18
+ ## Installation
19
+
20
+ Install the package using pnpm (recommended):
21
+
22
+ ```bash
23
+ pnpm add -D permaweb-deploy
24
+ ```
25
+
26
+ Or with npm:
27
+
28
+ ```bash
29
+ npm install --save-dev permaweb-deploy
30
+ ```
31
+
32
+ Or with yarn:
33
+
34
+ ```bash
35
+ yarn add --dev permaweb-deploy
36
+ ```
37
+
38
+ ## Prerequisites
39
+
40
+ 1. **For Arweave signer (default):** Encode your Arweave wallet key in base64 format and set it as a GitHub secret:
41
+
42
+ ```bash
43
+ base64 -i wallet.json | pbcopy
44
+ ```
45
+
46
+ 2. **For Ethereum/Polygon/KYVE signers:** Use your raw private key (no encoding needed) as the `DEPLOY_KEY`.
47
+
48
+ 3. Ensure that the secret name for the encoded wallet or private key is `DEPLOY_KEY`.
49
+
50
+ ⚠️ **Important:** Use a dedicated wallet for deployments to minimize security risks. Ensure your wallet has sufficient Turbo Credits for uploads.
51
+
52
+ ## Usage
53
+
54
+ ### Interactive Mode (Easiest)
55
+
56
+ **Command Menu:**
57
+
58
+ Simply run the CLI for an interactive command selector:
59
+
60
+ ```bash
61
+ permaweb-deploy
62
+ # or explicitly
63
+ permaweb-deploy interactive
64
+ ```
65
+
66
+ This shows a menu with options:
67
+ - **Deploy to Permaweb** - Start the deployment wizard
68
+ - **Show Help** - Display help information
69
+ - **Exit** - Exit the CLI
70
+
71
+ **Interactive Deploy (Guided):**
72
+
73
+ Run the deploy command without arguments to be guided through all deployment options:
74
+
75
+ ```bash
76
+ permaweb-deploy deploy
77
+ ```
78
+
79
+ This will prompt you for:
80
+ - ArNS name
81
+ - Wallet method (file, string, or environment variable)
82
+ - Signer type (Arweave, Ethereum, Polygon, KYVE)
83
+ - What to deploy (folder or file)
84
+ - Advanced options (optional: undername, TTL, network)
85
+
86
+ ### Direct Commands
87
+
88
+ Use flags for faster, scriptable deployments:
89
+
90
+ ```bash
91
+ # Basic deployment with wallet file
92
+ permaweb-deploy deploy --arns-name my-app --wallet ./wallet.json
93
+ ```
94
+
95
+ Deploy using private key directly:
96
+
97
+ ```bash
98
+ permaweb-deploy deploy --arns-name my-app --private-key "$(cat wallet.json)"
99
+ ```
100
+
101
+ Deploy using environment variable:
102
+
103
+ ```bash
104
+ DEPLOY_KEY=$(base64 -i wallet.json) permaweb-deploy deploy --arns-name my-app
105
+ ```
106
+
107
+ Deploy a specific folder:
108
+
109
+ ```bash
110
+ permaweb-deploy deploy --arns-name my-app --wallet ./wallet.json --deploy-folder ./build
111
+ ```
112
+
113
+ Deploy a single file:
114
+
115
+ ```bash
116
+ permaweb-deploy deploy --arns-name my-app --wallet ./wallet.json --deploy-file ./path/to/file.txt
117
+ ```
118
+
119
+ ### Advanced Usage
120
+
121
+ Deploy to an undername (subdomain):
122
+
123
+ ```bash
124
+ permaweb-deploy deploy --arns-name my-app --wallet ./wallet.json --undername staging
125
+ ```
126
+
127
+ Deploy with a custom TTL:
128
+
129
+ ```bash
130
+ permaweb-deploy deploy --arns-name my-app --wallet ./wallet.json --ttl-seconds 7200
131
+ ```
132
+
133
+ Deploy using Ethereum wallet (file):
134
+
135
+ ```bash
136
+ permaweb-deploy deploy --arns-name my-app --sig-type ethereum --wallet ./private-key.txt
137
+ ```
138
+
139
+ Deploy using Ethereum wallet (direct key):
140
+
141
+ ```bash
142
+ permaweb-deploy deploy --arns-name my-app --sig-type ethereum --private-key "0x1234..."
143
+ ```
144
+
145
+ ### Command Options
146
+
147
+ - `--arns-name, -n` (required): The ArNS name to update
148
+ - `--ario-process, -p`: ARIO process to use (`mainnet`, `testnet`, or a custom process ID). Default: `mainnet`
149
+ - `--deploy-folder, -d`: Folder to deploy. Default: `./dist`
150
+ - `--deploy-file, -f`: Deploy a single file instead of a folder
151
+ - `--undername, -u`: ANT undername to update. Default: `@`
152
+ - `--ttl-seconds, -t`: TTL in seconds for the ANT record (60-86400). Default: `60`
153
+ - `--sig-type, -s`: Signer type for deployment. Choices: `arweave`, `ethereum`, `polygon`, `kyve`. Default: `arweave`
154
+ - `--wallet, -w`: Path to wallet file (JWK for Arweave, private key for Ethereum/Polygon/KYVE)
155
+ - `--private-key, -k`: Private key or JWK JSON string (alternative to `--wallet`)
156
+
157
+ ### Package.json Scripts
158
+
159
+ Add deployment scripts to your `package.json`:
160
+
161
+ ```json
162
+ {
163
+ "scripts": {
164
+ "build": "vite build",
165
+ "deploy": "pnpm build && permaweb-deploy deploy --arns-name <ARNS_NAME>",
166
+ "deploy:staging": "pnpm build && permaweb-deploy deploy --arns-name <ARNS_NAME> --undername staging",
167
+ "deploy:testnet": "pnpm build && permaweb-deploy deploy --arns-name <ARNS_NAME> --ario-process testnet"
168
+ }
169
+ }
170
+ ```
171
+
172
+ Then deploy with:
173
+
174
+ ```bash
175
+ DEPLOY_KEY=$(base64 -i wallet.json) pnpm deploy
176
+ ```
177
+
178
+ ## GitHub Actions Workflow
179
+
180
+ To automate deployments, set up a GitHub Actions workflow:
181
+
182
+ ```yaml
183
+ name: Deploy to Permaweb
184
+
185
+ on:
186
+ push:
187
+ branches:
188
+ - main
189
+
190
+ jobs:
191
+ publish:
192
+ runs-on: ubuntu-latest
193
+ steps:
194
+ - uses: actions/checkout@v4
195
+
196
+ - uses: pnpm/action-setup@v3
197
+ with:
198
+ version: 9
199
+
200
+ - uses: actions/setup-node@v4
201
+ with:
202
+ node-version: 20
203
+ cache: 'pnpm'
204
+
205
+ - run: pnpm install
206
+
207
+ - run: pnpm deploy
208
+ env:
209
+ DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
210
+ ```
211
+
212
+ ## Development
213
+
214
+ ### Setup
215
+
216
+ ```bash
217
+ # Install dependencies
218
+ pnpm install
219
+
220
+ # Build the project
221
+ pnpm build
222
+
223
+ # Run in development mode
224
+ pnpm dev
225
+
226
+ # Run tests
227
+ pnpm test
228
+
229
+ # Run linter
230
+ pnpm lint
231
+
232
+ # Format code
233
+ pnpm format
234
+ ```
235
+
236
+ ### Project Structure
237
+
238
+ ```
239
+ permaweb-deploy/
240
+ ├── src/
241
+ │ ├── commands/ # oclif commands
242
+ │ │ └── deploy.ts
243
+ │ ├── types/ # TypeScript type definitions
244
+ │ │ └── index.ts
245
+ │ ├── utils/ # Utility functions
246
+ │ │ ├── constants.ts
247
+ │ │ ├── signer.ts
248
+ │ │ ├── uploader.ts
249
+ │ │ └── __tests__/ # Unit tests
250
+ │ └── index.ts # Main entry point
251
+ ├── bin/ # Executable scripts
252
+ │ ├── run.js
253
+ │ └── dev.js
254
+ ├── .changeset/ # Changesets configuration
255
+ ├── .husky/ # Git hooks
256
+ └── dist/ # Build output
257
+ ```
258
+
259
+ ## Security & Best Practices
260
+
261
+ - **Dedicated Wallet:** Always use a dedicated wallet for deployments to minimize security risks
262
+ - **Wallet Encoding:** Arweave wallets must be base64 encoded to be used in the deployment script
263
+ - **ArNS Name:** The ArNS Name must be passed so that the ANT Process can be resolved to update the target undername or root record
264
+ - **Turbo Credits:** Ensure your wallet has sufficient Turbo Credits before deployment
265
+ - **Secret Management:** Keep your `DEPLOY_KEY` secret secure and never commit it to your repository
266
+ - **Build Security:** Always check your build for exposed environmental secrets before deployment, as data on Arweave is permanent
267
+
268
+ ## Troubleshooting
269
+
270
+ - **Error: "DEPLOY_KEY environment variable not set":** Verify your base64 encoded wallet is set as the `DEPLOY_KEY` environment variable
271
+ - **Error: "deploy-folder does not exist":** Check that your build folder exists and the path is correct
272
+ - **Error: "deploy-file does not exist":** Check that your build file exists and the path is correct
273
+ - **Error: "ArNS name does not exist":** Verify the ArNS name is correct and exists in the specified network
274
+ - **Upload timeouts:** Files have a timeout for upload. Large files may fail and require optimization
275
+ - **Insufficient Turbo Credits:** Ensure your wallet has enough Turbo Credits for the deployment
276
+
277
+ ## Contributing
278
+
279
+ Contributions are welcome! Please follow these guidelines:
280
+
281
+ 1. Fork the repository
282
+ 2. Create a feature branch
283
+ 3. Make your changes
284
+ 4. Run tests and linter: `pnpm test && pnpm lint`
285
+ 5. Create a changeset: `pnpm changeset`
286
+ 6. Commit your changes using conventional commits
287
+ 7. Push and create a pull request
288
+
289
+ ### Conventional Commits
290
+
291
+ This project uses [Conventional Commits](https://www.conventionalcommits.org/). Commit messages should follow this format:
292
+
293
+ ```
294
+ type(scope): subject
295
+
296
+ body (optional)
297
+ ```
298
+
299
+ Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
300
+
301
+ ### Changesets
302
+
303
+ We use [changesets](https://github.com/changesets/changesets) for version management. When making changes:
304
+
305
+ ```bash
306
+ pnpm changeset
307
+ ```
308
+
309
+ Follow the prompts to describe your changes.
310
+
311
+ ## Dependencies
312
+
313
+ - **@ar.io/sdk** - For ANT operations and ArNS management
314
+ - **@ardrive/turbo-sdk** - For fast file uploads to Arweave
315
+ - **@permaweb/aoconnect** - For AO network connectivity
316
+ - **@oclif/core** - CLI framework
317
+ - **mime-types** - MIME type detection
318
+
319
+ ## License
320
+
321
+ ISC
322
+
323
+ ## Author
324
+
325
+ NickJ202
326
+
327
+ ## Links
328
+
329
+ - [GitHub Repository](https://github.com/permaweb/permaweb-deploy)
330
+ - [Issues](https://github.com/permaweb/permaweb-deploy/issues)
331
+ - [Arweave Documentation](https://docs.arweave.org/)
332
+ - [AR.IO Documentation](https://docs.ar.io/)
package/bin/dev.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env tsx
2
+
3
+ import { execute } from '@oclif/core'
4
+
5
+ await execute({ development: true, dir: import.meta.url })
6
+
package/bin/run.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execute } from '@oclif/core'
4
+
5
+ await execute({ development: false, dir: import.meta.url })
6
+
package/dist/index.js ADDED
@@ -0,0 +1,299 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
5
+ var _turboSdk = require("@ardrive/turbo-sdk");
6
+ var _fs = _interopRequireDefault(require("fs"));
7
+ var _yargs = _interopRequireDefault(require("yargs"));
8
+ var _helpers = require("yargs/helpers");
9
+ var _mimeTypes = _interopRequireDefault(require("mime-types"));
10
+ var _sdk = require("@ar.io/sdk");
11
+ var _aoconnect = require("@permaweb/aoconnect");
12
+ var _nodeStream = require("node:stream");
13
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
14
+ function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
15
+ function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { if (r) i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n;else { var o = function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); }; o("next", 0), o("throw", 1), o("return", 2); } }, _regeneratorDefine2(e, r, n, t); }
16
+ function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
17
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
18
+ function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
19
+ function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
20
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
21
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
22
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
23
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
24
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
25
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
26
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
27
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
28
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
29
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
30
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
31
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
32
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
33
+ var arweaveTxIdRegex = /^[a-zA-Z0-9-_]{43}$/;
34
+ var argv = (0, _yargs["default"])((0, _helpers.hideBin)(process.argv)).version('2.1.0').help().usage('Usage: $0 --arns-name <name> [options]').example('$0 --arns-name my-app', 'Deploy to my-app.arweave.dev').example('$0 --arns-name my-app --undername staging', 'Deploy to staging.my-app.arweave.dev').option('ario-process', {
35
+ alias: 'p',
36
+ type: 'string',
37
+ description: 'The ARIO process to use',
38
+ demandOption: true,
39
+ "default": _sdk.ARIO_MAINNET_PROCESS_ID
40
+ }).option('arns-name', {
41
+ alias: 'n',
42
+ type: 'string',
43
+ description: 'The ARNS name',
44
+ demandOption: true
45
+ }).option('deploy-folder', {
46
+ alias: 'd',
47
+ type: 'string',
48
+ description: 'Folder to deploy.',
49
+ "default": './dist'
50
+ }).option('deploy-file', {
51
+ alias: 'f',
52
+ type: 'string',
53
+ description: 'File to deploy.'
54
+ }).option('ttl-seconds', {
55
+ alias: 't',
56
+ type: 'number',
57
+ description: 'ArNS TTL Seconds',
58
+ "default": 60
59
+ }).option('undername', {
60
+ alias: 'u',
61
+ type: 'string',
62
+ description: 'ANT undername to update.',
63
+ "default": '@'
64
+ }).option('sig-type', {
65
+ alias: 's',
66
+ type: 'string',
67
+ description: 'The type of signer to be used for deployment.',
68
+ choices: ['arweave', 'ethereum', 'polygon',
69
+ // 'solana',
70
+ 'kyve'],
71
+ "default": 'arweave'
72
+ }).check(function (argv) {
73
+ if (argv.ttl < 60 || argv.ttl > 86400) {
74
+ throw new Error('TTL must be between 60 seconds (1 minute) and 86400 seconds (1 day)');
75
+ }
76
+ return true;
77
+ }).argv;
78
+ var DEPLOY_KEY = process.env.DEPLOY_KEY;
79
+ var ARNS_NAME = argv['arns-name'];
80
+ var ARIO_PROCESS = argv['ario-process'];
81
+ var TTL_SECONDS = argv['ttl-seconds'];
82
+ if (ARIO_PROCESS === 'mainnet') {
83
+ ARIO_PROCESS = _sdk.ARIO_MAINNET_PROCESS_ID;
84
+ } else if (ARIO_PROCESS === 'testnet') {
85
+ ARIO_PROCESS = _sdk.ARIO_TESTNET_PROCESS_ID;
86
+ }
87
+ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
88
+ var ario, arnsNameRecord, signer, token, jwk, turbo, uploadResult, txOrManifestId, mimeType, origPaths, newPaths, replaceManifest, _i, _Object$entries, _Object$entries$_i, key, value, newKey, newManifest, buffer, _yield$turbo$uploadFi, id, ant, _t, _t2;
89
+ return _regenerator().w(function (_context) {
90
+ while (1) switch (_context.n) {
91
+ case 0:
92
+ if (!ARIO_PROCESS || !arweaveTxIdRegex.test(ARIO_PROCESS)) {
93
+ console.error('ARIO_PROCESS must be a valid Arweave transaction ID, or "mainnet" or "testnet"');
94
+ process.exit(1);
95
+ }
96
+ if (!DEPLOY_KEY) {
97
+ console.error('DEPLOY_KEY not configured');
98
+ process.exit(1);
99
+ }
100
+ if (!ARNS_NAME) {
101
+ console.error('ARNS_NAME not configured');
102
+ process.exit(1);
103
+ }
104
+ if (!Number.isFinite(TTL_SECONDS) || TTL_SECONDS < 60 || TTL_SECONDS > 86400) {
105
+ console.error('TTL_SECONDS must be a number between 60 and 86400 seconds');
106
+ process.exit(1);
107
+ }
108
+ if (argv.deployFile && !_fs["default"].existsSync(argv.deployFile)) {
109
+ console.error("deploy-file [".concat(argv.deployFolder, "] does not exist"));
110
+ process.exit(1);
111
+ } else {
112
+ if (!_fs["default"].existsSync(argv.deployFolder)) {
113
+ console.error("deploy-folder [".concat(argv.deployFolder, "] does not exist"));
114
+ process.exit(1);
115
+ }
116
+ }
117
+ if (argv.undername.length === 0) {
118
+ console.error('undername must be set');
119
+ process.exit(1);
120
+ }
121
+ ario = _sdk.ARIO.init({
122
+ process: new _sdk.AOProcess({
123
+ processId: ARIO_PROCESS,
124
+ ao: (0, _aoconnect.connect)({
125
+ MODE: 'legacy',
126
+ CU_URL: 'https://cu.ardrive.io'
127
+ })
128
+ })
129
+ });
130
+ _context.n = 1;
131
+ return ario.getArNSRecord({
132
+ name: ARNS_NAME
133
+ })["catch"](function (e) {
134
+ console.error("ARNS name [".concat(ARNS_NAME, "] does not exist"));
135
+ process.exit(1);
136
+ });
137
+ case 1:
138
+ arnsNameRecord = _context.v;
139
+ _context.p = 2;
140
+ _t = argv['sig-type'];
141
+ _context.n = _t === 'ethereum' ? 3 : _t === 'polygon' ? 4 : _t === 'arweave' ? 5 : _t === 'kyve' ? 6 : 7;
142
+ break;
143
+ case 3:
144
+ signer = new _turboSdk.EthereumSigner(DEPLOY_KEY);
145
+ token = 'ethereum';
146
+ return _context.a(3, 8);
147
+ case 4:
148
+ signer = new _turboSdk.EthereumSigner(DEPLOY_KEY);
149
+ token = 'pol';
150
+ return _context.a(3, 8);
151
+ case 5:
152
+ jwk = JSON.parse(Buffer.from(DEPLOY_KEY, 'base64').toString('utf-8'));
153
+ signer = new _sdk.ArweaveSigner(jwk);
154
+ token = 'arweave';
155
+ return _context.a(3, 8);
156
+ case 6:
157
+ signer = new _turboSdk.EthereumSigner(DEPLOY_KEY);
158
+ token = 'kyve';
159
+ return _context.a(3, 8);
160
+ case 7:
161
+ throw new Error("Invalid sig-type provided: ".concat(argv['sig-type'], ". Allowed values are 'arweave', 'ethereum', 'polygon', or 'kyve'."));
162
+ case 8:
163
+ turbo = _turboSdk.TurboFactory.authenticated({
164
+ signer: signer,
165
+ token: token
166
+ });
167
+ if (!argv['deploy-file']) {
168
+ _context.n = 10;
169
+ break;
170
+ }
171
+ // Detect MIME type for the file
172
+ mimeType = _mimeTypes["default"].lookup(argv['deploy-file']) || 'application/octet-stream';
173
+ _context.n = 9;
174
+ return turbo.uploadFile({
175
+ file: argv['deploy-file'],
176
+ dataItemOpts: {
177
+ tags: [{
178
+ name: 'App-Name',
179
+ value: 'Permaweb-Deploy'
180
+ },
181
+ // prevents identical transaction Ids from eth wallets
182
+ {
183
+ name: 'anchor',
184
+ value: new Date().toISOString()
185
+ }, {
186
+ name: 'Content-Type',
187
+ value: mimeType
188
+ }]
189
+ }
190
+ });
191
+ case 9:
192
+ uploadResult = _context.v;
193
+ txOrManifestId = uploadResult.id;
194
+ _context.n = 13;
195
+ break;
196
+ case 10:
197
+ _context.n = 11;
198
+ return turbo.uploadFolder({
199
+ folderPath: argv['deploy-folder'],
200
+ dataItemOpts: {
201
+ tags: [{
202
+ name: 'App-Name',
203
+ value: 'Permaweb-Deploy'
204
+ },
205
+ // prevents identical transaction Ids from eth wallets
206
+ {
207
+ name: 'anchor',
208
+ value: new Date().toISOString()
209
+ }]
210
+ }
211
+ });
212
+ case 11:
213
+ uploadResult = _context.v;
214
+ txOrManifestId = uploadResult.manifestResponse.id; //might replace now
215
+
216
+ // Make default folder paths work by adding extra path entries
217
+ origPaths = uploadResult.manifest.paths;
218
+ newPaths = {};
219
+ replaceManifest = false;
220
+ for (_i = 0, _Object$entries = Object.entries(origPaths); _i < _Object$entries.length; _i++) {
221
+ _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), key = _Object$entries$_i[0], value = _Object$entries$_i[1];
222
+ newPaths[key] = value;
223
+ if (key.endsWith('/index.html')) {
224
+ newKey = key.replace(/\/index\.html$/, '');
225
+ newPaths[newKey] = value;
226
+ replaceManifest = true;
227
+ }
228
+ ;
229
+ }
230
+ ;
231
+ if (!replaceManifest) {
232
+ _context.n = 13;
233
+ break;
234
+ }
235
+ console.info('replacing manifest');
236
+ newManifest = _objectSpread(_objectSpread({}, uploadResult.manifest), {}, {
237
+ paths: newPaths
238
+ });
239
+ buffer = Buffer.from(JSON.stringify(newManifest));
240
+ _context.n = 12;
241
+ return turbo.uploadFile({
242
+ fileStreamFactory: function fileStreamFactory() {
243
+ return _nodeStream.Readable.from(buffer);
244
+ },
245
+ fileSizeFactory: function fileSizeFactory() {
246
+ return buffer.length;
247
+ },
248
+ dataItemOpts: {
249
+ tags: [{
250
+ name: 'Content-Type',
251
+ value: 'application/x.arweave-manifest+json'
252
+ }]
253
+ }
254
+ });
255
+ case 12:
256
+ _yield$turbo$uploadFi = _context.v;
257
+ id = _yield$turbo$uploadFi.id;
258
+ txOrManifestId = id;
259
+ case 13:
260
+ console.log('-------------------- DEPLOY DETAILS --------------------');
261
+ console.log("Tx ID: ".concat(txOrManifestId));
262
+ console.log("ArNS Name: ".concat(ARNS_NAME));
263
+ console.log("Undername: ".concat(argv.undername));
264
+ console.log("ANT: ".concat(arnsNameRecord.processId));
265
+ console.log("AR IO Process: ".concat(ARIO_PROCESS));
266
+ console.log("TTL Seconds: ".concat(TTL_SECONDS));
267
+ console.log('--------------------------------------------------------');
268
+ ant = _sdk.ANT.init({
269
+ processId: arnsNameRecord.processId,
270
+ signer: signer
271
+ }); // Update the ANT record (assumes the JWK is a controller or owner)
272
+ _context.n = 14;
273
+ return ant.setRecord({
274
+ undername: argv.undername,
275
+ transactionId: txOrManifestId,
276
+ ttlSeconds: argv['ttl-seconds']
277
+ }, {
278
+ tags: [{
279
+ name: 'App-Name',
280
+ value: 'Permaweb-Deploy'
281
+ }].concat(_toConsumableArray(process.env.GITHUB_SHA ? [{
282
+ name: 'GIT-HASH',
283
+ value: process.env.GITHUB_SHA
284
+ }] : []))
285
+ });
286
+ case 14:
287
+ console.log("Deployed TxId [".concat(txOrManifestId, "] to name [").concat(ARNS_NAME, "] for ANT [").concat(arnsNameRecord.processId, "] using undername [").concat(argv.undername, "]"));
288
+ _context.n = 16;
289
+ break;
290
+ case 15:
291
+ _context.p = 15;
292
+ _t2 = _context.v;
293
+ console.error('Deployment failed:', _t2);
294
+ process.exit(1); // Exit with error code
295
+ case 16:
296
+ return _context.a(2);
297
+ }
298
+ }, _callee, null, [[2, 15]]);
299
+ }))();
package/package.json ADDED
@@ -0,0 +1,96 @@
1
+ {
2
+ "name": "permaweb-deploy",
3
+ "version": "0.0.0-rc-20251001215907",
4
+ "description": "Permaweb App Deployment Package",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "module",
8
+ "bin": {
9
+ "permaweb-deploy": "./bin/run.js"
10
+ },
11
+ "oclif": {
12
+ "bin": "permaweb-deploy",
13
+ "dirname": "permaweb-deploy",
14
+ "commands": "./dist/commands",
15
+ "topicSeparator": " ",
16
+ "default": "interactive",
17
+ "topics": {
18
+ "deploy": {
19
+ "description": "Deploy to the permaweb"
20
+ }
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "bin",
26
+ "oclif.manifest.json"
27
+ ],
28
+ "dependencies": {
29
+ "@ar.io/sdk": "^3.10.1",
30
+ "@ardrive/turbo-sdk": "^1.28.2",
31
+ "@inquirer/prompts": "^7.2.0",
32
+ "@oclif/core": "^4.0.30",
33
+ "@permaweb/aoconnect": "^0.0.85",
34
+ "boxen": "^8.0.1",
35
+ "chalk": "^5.3.0",
36
+ "cli-table3": "^0.6.5",
37
+ "mime-types": "^3.0.1",
38
+ "ora": "^8.1.1"
39
+ },
40
+ "devDependencies": {
41
+ "@changesets/cli": "^2.27.10",
42
+ "@commitlint/cli": "^19.6.0",
43
+ "@commitlint/config-conventional": "^19.6.0",
44
+ "@oclif/prettier-config": "^0.2.1",
45
+ "@types/mime-types": "^2.1.4",
46
+ "@types/node": "^22.10.2",
47
+ "@typescript-eslint/eslint-plugin": "^8.18.1",
48
+ "@typescript-eslint/parser": "^8.18.1",
49
+ "@vitest/coverage-v8": "^2.1.8",
50
+ "eslint": "^8.57.1",
51
+ "eslint-config-oclif": "^5.2.1",
52
+ "eslint-config-oclif-typescript": "^3.1.12",
53
+ "eslint-config-prettier": "^9.1.0",
54
+ "eslint-import-resolver-typescript": "^3.7.0",
55
+ "eslint-plugin-import": "^2.31.0",
56
+ "eslint-plugin-prettier": "^5.2.1",
57
+ "eslint-plugin-simple-import-sort": "^12.1.1",
58
+ "husky": "^9.1.7",
59
+ "prettier": "^3.4.2",
60
+ "tsx": "^4.19.2",
61
+ "typescript": "^5.7.2",
62
+ "vite": "^6.0.5",
63
+ "vite-plugin-dts": "^4.3.0",
64
+ "vite-tsconfig-paths": "^5.1.4",
65
+ "vitest": "^2.1.8"
66
+ },
67
+ "engines": {
68
+ "node": ">=18.0.0"
69
+ },
70
+ "repository": {
71
+ "type": "git",
72
+ "url": "git+https://github.com/permaweb/permaweb-deploy.git"
73
+ },
74
+ "author": "NickJ202",
75
+ "license": "ISC",
76
+ "bugs": {
77
+ "url": "https://github.com/permaweb/permaweb-deploy/issues"
78
+ },
79
+ "homepage": "https://github.com/permaweb/permaweb-deploy#readme",
80
+ "scripts": {
81
+ "build": "vite build && tsc --emitDeclarationOnly",
82
+ "dev": "tsx src/index.ts",
83
+ "test": "vitest",
84
+ "test:run": "vitest run",
85
+ "test:coverage": "vitest run --coverage",
86
+ "lint": "eslint . --ext .ts",
87
+ "lint:fix": "eslint . --ext .ts --fix",
88
+ "format": "prettier --write \"src/**/*.ts\"",
89
+ "format:check": "prettier --check \"src/**/*.ts\"",
90
+ "changeset": "changeset",
91
+ "version": "changeset version",
92
+ "version:alpha": "changeset version --snapshot alpha",
93
+ "release": "changeset publish",
94
+ "release:alpha": "changeset publish --tag alpha"
95
+ }
96
+ }