permaweb-deploy 2.3.0 → 2.4.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Permaweb Deployment Package
2
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 simplifies the process by uploading your build folder, creating Arweave manifests, and updating ArNS (Arweave Name Service) records via ANT (Arweave Name Token) with the transaction ID.
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
4
 
5
5
  ### Features
6
6
 
@@ -11,6 +11,7 @@ Inspired by the [cookbook github action deployment guide](https://cookbook.arwea
11
11
  - **Git Hash Tagging:** Automatically tags deployments with Git commit hashes
12
12
  - **404 Fallback Detection:** Automatically detects and sets 404.html as fallback
13
13
  - **Network Support:** Supports mainnet, testnet, and custom ARIO process IDs
14
+ - **Flexible Deployment:** Supports deploying a folder or a single file
14
15
 
15
16
  ### Installation
16
17
 
@@ -34,32 +35,17 @@ yarn add permaweb-deploy --dev --ignore-engines
34
35
 
35
36
  ### Prerequisites
36
37
 
37
- Before using `permaweb-deploy`, you must:
38
+ 1. **For Arweave signer (default):** Encode your Arweave wallet key in base64 format and set it as a GitHub secret:
38
39
 
39
- 1. **Arweave Wallet:** Have an Arweave wallet with Turbo Credits for uploading
40
- 2. **ArNS Name:** Own or control an ArNS name (which has an associated ANT process)
41
- 3. **Wallet Encoding:** Encode your Arweave wallet key in base64 format:
42
40
  ```bash
43
41
  base64 -i wallet.json | pbcopy
44
42
  ```
45
- 4. **GitHub Secret:** Set the encoded wallet as a GitHub secret named `DEPLOY_KEY`
46
43
 
47
- ⚠️ **Important:** Use a dedicated wallet for deployments to minimize security risks. Ensure your wallet has sufficient Turbo Credits for uploads.
44
+ 2. **For Ethereum/Polygon/KYVE signers:** Use your raw private key (no encoding needed) as the `DEPLOY_KEY`.
48
45
 
49
- ### CLI Options
46
+ 3. Ensure that the secret name for the encoded wallet or private key is `DEPLOY_KEY`.
50
47
 
51
- ```bash
52
- permaweb-deploy [options]
53
- ```
54
-
55
- | Option | Alias | Description | Default | Required |
56
- | ----------------- | ----- | -------------------------------------- | --------- | -------- |
57
- | `--arns-name` | `-n` | ArNS name for deployment | - | ✅ |
58
- | `--ario-process` | `-p` | ARIO process ID or "mainnet"/"testnet" | `mainnet` | ❌ |
59
- | `--deploy-folder` | `-d` | Folder to deploy | `./dist` | ❌ |
60
- | `--deploy-file` | `-f` | File to deploy | `./dist` | ❌ |
61
- | `--undername` | `-u` | ANT undername to update | `@` | ❌ |
62
- | `--ttl-seconds` | `-t` | ArNS TTL Seconds | `3600` | ❌ |
48
+ ⚠️ **Important:** Use a dedicated wallet for deployments to minimize security risks. Ensure your wallet has sufficient Turbo Credits for uploads.
63
49
 
64
50
  ### Usage
65
51
 
@@ -68,73 +54,107 @@ To deploy your application, ensure you have a build script and a deployment scri
68
54
  ```json
69
55
  "scripts": {
70
56
  "build": "your-build-command",
71
- "deploy-main": "npm run build && permaweb-deploy --arns-name <ARNS_NAME>"
57
+ "deploy": "npm run build && permaweb-deploy --arns-name <ARNS_NAME>"
72
58
  }
73
59
  ```
74
60
 
75
- **Example with custom options:**
61
+ Replace `<ARNS_NAME>` with your ArNS name. To deploy to an undername, add `--undername <UNDERNAME>`.
76
62
 
77
- ```bash
78
- permaweb-deploy --arns-name "your-arns-name" --deploy-folder "./build" --undername "app"
63
+ #### CLI Options
64
+
65
+ - `--arns-name, -n` (required): The ArNS name to update.
66
+ - `--ario-process, -p`: ARIO process to use (`mainnet`, `testnet`, or a custom process ID). Default: mainnet.
67
+ - `--deploy-folder, -d`: Folder to deploy. Default: `./dist`.
68
+ - `--deploy-file, -f`: Deploy a single file instead of a folder.
69
+ - `--undername, -u`: ANT undername to update. Default: `@`.
70
+ - `--ttl-seconds, -t`: TTL in seconds for the ANT record (60-86400). Default: `3600`.
71
+ - `--sig-type, -s`: Signer type for deployment. Choices: `arweave`, `ethereum`, `polygon`, `kyve`. Default: `arweave`.
72
+ - `--help`: Show all available options and usage examples.
73
+ - `--version`: Show the current version number.
74
+
75
+ #### Example CLI Usage
76
+
77
+ Deploy a folder (default):
78
+
79
+ ```sh
80
+ DEPLOY_KEY=$(base64 -i wallet.json) npx permaweb-deploy --arns-name my-app
81
+ ```
82
+
83
+ Deploy a specific folder:
84
+
85
+ ```sh
86
+ DEPLOY_KEY=$(base64 -i wallet.json) npx permaweb-deploy --arns-name my-app --deploy-folder ./build
87
+ ```
88
+
89
+ Deploy a single file:
90
+
91
+ ```sh
92
+ DEPLOY_KEY=$(base64 -i wallet.json) npx permaweb-deploy --arns-name my-app --deploy-file ./path/to/file.txt
93
+ ```
94
+
95
+ Deploy to an undername:
96
+
97
+ ```sh
98
+ DEPLOY_KEY=$(base64 -i wallet.json) npx permaweb-deploy --arns-name my-app --undername staging
79
99
  ```
80
100
 
81
- Replace `<ARNS_NAME>` with your ArNS name. You can also specify testnet, mainnet, and custom process IDs for the ARIO process to use.
101
+ Deploy with a custom TTL:
82
102
 
83
- **Mainnet (default) config:**
103
+ ```sh
104
+ DEPLOY_KEY=$(base64 -i wallet.json) npx permaweb-deploy --arns-name my-app --ttl-seconds 7200
105
+ ```
106
+
107
+ Deploy using a different signer (e.g., Ethereum):
108
+
109
+ ```sh
110
+ DEPLOY_KEY=<ETH_PRIVATE_KEY> npx permaweb-deploy --arns-name my-app --sig-type ethereum
111
+ ```
112
+
113
+ #### Example `package.json` Scripts
84
114
 
85
115
  ```json
86
116
  "scripts": {
87
- "build": "your-build-command",
88
- "deploy-main": "npm run build && permaweb-deploy --arns-name <ARNS_NAME> --ario-process mainnet"
117
+ "build": "vite build",
118
+ "deploy": "npm run build && permaweb-deploy --arns-name <ARNS_NAME>"
89
119
  }
90
120
  ```
91
121
 
92
- **Testnet config:**
122
+ #### ARIO Process Examples
123
+
124
+ **Mainnet (default):**
93
125
 
94
126
  ```json
95
127
  "scripts": {
96
- "build": "your-build-command",
97
- "deploy-main": "npm run build && permaweb-deploy --arns-name <ARNS_NAME> --ario-process testnet"
128
+ "deploy-main": "npm run build && permaweb-deploy --arns-name <ARNS_NAME>"
98
129
  }
99
130
  ```
100
131
 
101
- **Custom process ID config:**
132
+ **Testnet:**
102
133
 
103
134
  ```json
104
135
  "scripts": {
105
- "build": "your-build-command",
106
- "deploy-main": "npm run build && permaweb-deploy --arns-name <ARNS_NAME> --ario-process GaQrvEMKBpkjofgnBi_B3IgIDmY_XYelVLB6GcRGrHc"
136
+ "deploy-test": "npm run build && permaweb-deploy --arns-name <ARNS_NAME> --ario-process testnet"
107
137
  }
108
138
  ```
109
139
 
110
- ### Manual CLI Deployment
140
+ **Custom process ID:**
111
141
 
112
- ```bash
113
- DEPLOY_KEY=$(base64 -i wallet.json) npx permaweb-deploy --arns-name <ARNS_NAME>
142
+ ```json
143
+ "scripts": {
144
+ "deploy-custom": "npm run build && permaweb-deploy --arns-name <ARNS_NAME> --ario-process <PROCESS_ID>"
145
+ }
114
146
  ```
115
147
 
116
- ### Technical Details
117
-
118
- - **Upload Service:** Uses Turbo SDK for fast, reliable file uploads to Arweave
119
- - **Manifest Format:** Creates Arweave manifests using version 0.2.0 specification
120
- - **Fallback Support:** Automatically detects `404.html` and sets it as fallback, otherwise uses `index.html`
121
- - **Upload Timeout:** 10-second timeout per file upload for reliability
122
- - **ArNS Record TTL:** Sets 3600 seconds (1 hour) TTL for ArNS records via ANT
123
- - **Deployment Tags:** Automatically adds `App-Name: Permaweb-Deploy` and Git hash tags
124
- - **Network Support:** Supports mainnet, testnet, and custom ARIO process IDs
125
-
126
148
  ### GitHub Actions Workflow
127
149
 
128
150
  To automate the deployment, set up a GitHub Actions workflow as follows:
129
151
 
130
152
  ```yaml
131
- name: publish
132
-
153
+ name: Deploy to Permaweb
133
154
  on:
134
155
  push:
135
156
  branches:
136
- - 'main'
137
-
157
+ - main
138
158
  jobs:
139
159
  publish:
140
160
  runs-on: ubuntu-latest
@@ -144,15 +164,16 @@ jobs:
144
164
  with:
145
165
  node-version: 20.x
146
166
  - run: npm install
147
- - run: npm run deploy-main
167
+ - run: npm run deploy
148
168
  env:
149
169
  DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
150
170
  ```
151
171
 
172
+
152
173
  ### Security & Best Practices
153
174
 
154
175
  - **Dedicated Wallet:** Always use a dedicated wallet for deployments to minimize security risks
155
- - **Wallet Encoding:** The wallet must be base64 encoded to be used in the deployment script
176
+ - **Wallet Encoding:** Arweave wallets must be base64 encoded to be used in the deployment script
156
177
  - **ArNS Name:** The ArNS Name must be passed so that the ANT Process can be resolved to update the target undername or root record
157
178
  - **Turbo Credits:** Ensure your wallet has sufficient Turbo Credits before deployment
158
179
  - **Secret Management:** Keep your `DEPLOY_KEY` secret secure and never commit it to your repository
@@ -173,5 +194,4 @@ jobs:
173
194
  - **@ar.io/sdk:** - For ANT operations and ArNS management
174
195
  - **@ardrive/turbo-sdk:** - For fast file uploads to Arweave
175
196
  - **@permaweb/aoconnect:** - For AO network connectivity
176
- - **mime-types:** - For automatic content type detection
177
197
  - **yargs:** - For CLI argument parsing
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
 
4
+ var _turboSdk = require("@ardrive/turbo-sdk");
4
5
  var _fs = _interopRequireDefault(require("fs"));
5
6
  var _yargs = _interopRequireDefault(require("yargs"));
6
7
  var _helpers = require("yargs/helpers");
8
+ var _mimeTypes = _interopRequireDefault(require("mime-types"));
7
9
  var _sdk = require("@ar.io/sdk");
8
- var _turboSdk = require("@ardrive/turbo-sdk");
9
10
  var _aoconnect = require("@permaweb/aoconnect");
10
- var _turbo = require("./turbo");
11
11
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
12
12
  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 }; })(); }
13
13
  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); }
@@ -20,7 +20,7 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
20
20
  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); }
21
21
  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); }); }; }
22
22
  var arweaveTxIdRegex = /^[a-zA-Z0-9-_]{43}$/;
23
- var argv = (0, _yargs["default"])((0, _helpers.hideBin)(process.argv)).option('ario-process', {
23
+ 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', {
24
24
  alias: 'p',
25
25
  type: 'string',
26
26
  description: 'The ARIO process to use',
@@ -50,18 +50,31 @@ var argv = (0, _yargs["default"])((0, _helpers.hideBin)(process.argv)).option('a
50
50
  type: 'string',
51
51
  description: 'ANT undername to update.',
52
52
  "default": '@'
53
+ }).option('sig-type', {
54
+ alias: 's',
55
+ type: 'string',
56
+ description: 'The type of signer to be used for deployment.',
57
+ choices: ['arweave', 'ethereum', 'polygon',
58
+ // 'solana',
59
+ 'kyve'],
60
+ "default": 'arweave'
61
+ }).check(function (argv) {
62
+ if (argv.ttl < 60 || argv.ttl > 86400) {
63
+ throw new Error('TTL must be between 60 seconds (1 minute) and 86400 seconds (1 day)');
64
+ }
65
+ return true;
53
66
  }).argv;
54
67
  var DEPLOY_KEY = process.env.DEPLOY_KEY;
55
- var ARNS_NAME = argv.arnsName;
56
- var TTL_SECONDS = argv.ttlSeconds;
57
- var ARIO_PROCESS = argv.arioProcess;
68
+ var ARNS_NAME = argv['arns-name'];
69
+ var ARIO_PROCESS = argv['ario-process'];
70
+ var TTL_SECONDS = argv['ttl-seconds'];
58
71
  if (ARIO_PROCESS === 'mainnet') {
59
72
  ARIO_PROCESS = _sdk.ARIO_MAINNET_PROCESS_ID;
60
73
  } else if (ARIO_PROCESS === 'testnet') {
61
74
  ARIO_PROCESS = _sdk.ARIO_TESTNET_PROCESS_ID;
62
75
  }
63
76
  _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
64
- var jwk, ario, arnsNameRecord, txId, turbo, signer, ant, _t;
77
+ var ario, arnsNameRecord, signer, token, jwk, turbo, uploadResult, txOrManifestId, mimeType, ant, _t, _t2;
65
78
  return _regenerator().w(function (_context) {
66
79
  while (1) switch (_context.n) {
67
80
  case 0:
@@ -94,7 +107,6 @@ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
94
107
  console.error('undername must be set');
95
108
  process.exit(1);
96
109
  }
97
- jwk = JSON.parse(Buffer.from(DEPLOY_KEY, 'base64').toString('utf-8'));
98
110
  ario = _sdk.ARIO.init({
99
111
  process: new _sdk.AOProcess({
100
112
  processId: ARIO_PROCESS,
@@ -114,43 +126,99 @@ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
114
126
  case 1:
115
127
  arnsNameRecord = _context.v;
116
128
  _context.p = 2;
117
- if (!argv.deployFile) {
118
- _context.n = 4;
119
- break;
120
- }
121
- turbo = _turboSdk.TurboFactory.authenticated({
122
- privateKey: jwk
123
- });
124
- _context.n = 3;
125
- return (0, _turbo.uploadFile)(argv.deployFile, turbo);
126
- case 3:
127
- txId = _context.v.id;
128
- _context.n = 6;
129
+ _t = argv['sig-type'];
130
+ _context.n = _t === 'ethereum' ? 3 : _t === 'polygon' ? 4 : _t === 'arweave' ? 5 : _t === 'kyve' ? 6 : 7;
129
131
  break;
132
+ case 3:
133
+ signer = new _turboSdk.EthereumSigner(DEPLOY_KEY);
134
+ token = 'ethereum';
135
+ return _context.a(3, 8);
130
136
  case 4:
131
- _context.n = 5;
132
- return (0, _turbo.uploadDirectory)(argv, jwk);
137
+ signer = new _turboSdk.EthereumSigner(DEPLOY_KEY);
138
+ token = 'pol';
139
+ return _context.a(3, 8);
133
140
  case 5:
134
- txId = _context.v;
141
+ jwk = JSON.parse(Buffer.from(DEPLOY_KEY, 'base64').toString('utf-8'));
142
+ signer = new _sdk.ArweaveSigner(jwk);
143
+ token = 'arweave';
144
+ return _context.a(3, 8);
135
145
  case 6:
146
+ signer = new _turboSdk.EthereumSigner(DEPLOY_KEY);
147
+ token = 'kyve';
148
+ return _context.a(3, 8);
149
+ case 7:
150
+ throw new Error("Invalid sig-type provided: ".concat(argv['sig-type'], ". Allowed values are 'arweave', 'ethereum', 'polygon', or 'kyve'."));
151
+ case 8:
152
+ turbo = _turboSdk.TurboFactory.authenticated({
153
+ signer: signer,
154
+ token: token
155
+ });
156
+ if (!argv['deploy-file']) {
157
+ _context.n = 10;
158
+ break;
159
+ }
160
+ // Detect MIME type for the file
161
+ mimeType = _mimeTypes["default"].lookup(argv['deploy-file']) || 'application/octet-stream';
162
+ _context.n = 9;
163
+ return turbo.uploadFile({
164
+ file: argv['deploy-file'],
165
+ dataItemOpts: {
166
+ tags: [{
167
+ name: 'App-Name',
168
+ value: 'Permaweb-Deploy'
169
+ },
170
+ // prevents identical transaction Ids from eth wallets
171
+ {
172
+ name: 'anchor',
173
+ value: new Date().toISOString()
174
+ }, {
175
+ name: 'Content-Type',
176
+ value: mimeType
177
+ }]
178
+ }
179
+ });
180
+ case 9:
181
+ uploadResult = _context.v;
182
+ txOrManifestId = uploadResult.id;
183
+ _context.n = 12;
184
+ break;
185
+ case 10:
186
+ _context.n = 11;
187
+ return turbo.uploadFolder({
188
+ folderPath: argv['deploy-folder'],
189
+ dataItemOpts: {
190
+ tags: [{
191
+ name: 'App-Name',
192
+ value: 'Permaweb-Deploy'
193
+ },
194
+ // prevents identical transaction Ids from eth wallets
195
+ {
196
+ name: 'anchor',
197
+ value: new Date().toISOString()
198
+ }]
199
+ }
200
+ });
201
+ case 11:
202
+ uploadResult = _context.v;
203
+ txOrManifestId = uploadResult.manifestResponse.id;
204
+ case 12:
136
205
  console.log('-------------------- DEPLOY DETAILS --------------------');
137
- console.log("Tx ID: ".concat(txId));
206
+ console.log("Tx ID: ".concat(txOrManifestId));
138
207
  console.log("ArNS Name: ".concat(ARNS_NAME));
139
208
  console.log("Undername: ".concat(argv.undername));
140
209
  console.log("ANT: ".concat(arnsNameRecord.processId));
141
210
  console.log("AR IO Process: ".concat(ARIO_PROCESS));
142
211
  console.log("TTL Seconds: ".concat(TTL_SECONDS));
143
212
  console.log('--------------------------------------------------------');
144
- signer = new _sdk.ArweaveSigner(jwk);
145
213
  ant = _sdk.ANT.init({
146
214
  processId: arnsNameRecord.processId,
147
215
  signer: signer
148
216
  }); // Update the ANT record (assumes the JWK is a controller or owner)
149
- _context.n = 7;
217
+ _context.n = 13;
150
218
  return ant.setRecord({
151
219
  undername: argv.undername,
152
- transactionId: txId,
153
- ttlSeconds: TTL_SECONDS
220
+ transactionId: txOrManifestId,
221
+ ttlSeconds: argv['ttl-seconds']
154
222
  }, {
155
223
  tags: [{
156
224
  name: 'App-Name',
@@ -160,17 +228,17 @@ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
160
228
  value: process.env.GITHUB_SHA
161
229
  }] : []))
162
230
  });
163
- case 7:
164
- console.log("Deployed TxId [".concat(txId, "] to name [").concat(ARNS_NAME, "] for ANT [").concat(arnsNameRecord.processId, "] using undername [").concat(argv.undername, "]"));
165
- _context.n = 9;
231
+ case 13:
232
+ console.log("Deployed TxId [".concat(txOrManifestId, "] to name [").concat(ARNS_NAME, "] for ANT [").concat(arnsNameRecord.processId, "] using undername [").concat(argv.undername, "]"));
233
+ _context.n = 15;
166
234
  break;
167
- case 8:
168
- _context.p = 8;
169
- _t = _context.v;
170
- console.error('Deployment failed:', _t);
235
+ case 14:
236
+ _context.p = 14;
237
+ _t2 = _context.v;
238
+ console.error('Deployment failed:', _t2);
171
239
  process.exit(1); // Exit with error code
172
- case 9:
240
+ case 15:
173
241
  return _context.a(2);
174
242
  }
175
- }, _callee, null, [[2, 8]]);
243
+ }, _callee, null, [[2, 14]]);
176
244
  }))();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "permaweb-deploy",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Permaweb App Deployment Package",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -11,9 +11,9 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@ar.io/sdk": "^3.10.1",
14
- "@ardrive/turbo-sdk": "^1.17.0",
14
+ "@ardrive/turbo-sdk": "^1.28.2",
15
15
  "@permaweb/aoconnect": "^0.0.85",
16
- "mime-types": "^2.1.35",
16
+ "mime-types": "^3.0.1",
17
17
  "yargs": "17.7.2"
18
18
  },
19
19
  "devDependencies": {
package/src/index.js CHANGED
@@ -1,24 +1,27 @@
1
1
  #!/usr/bin/env node
2
-
2
+ import { EthereumSigner, TurboFactory } from '@ardrive/turbo-sdk';
3
3
  import fs from 'fs';
4
4
  import yargs from 'yargs';
5
5
  import { hideBin } from 'yargs/helpers';
6
+ import mime from 'mime-types';
6
7
 
7
8
  import { ANT, AOProcess, ARIO, ARIO_MAINNET_PROCESS_ID, ARIO_TESTNET_PROCESS_ID, ArweaveSigner } from '@ar.io/sdk';
8
- import { TurboFactory } from '@ardrive/turbo-sdk';
9
9
  import { connect } from '@permaweb/aoconnect';
10
10
 
11
- import { uploadDirectory, uploadFile } from './turbo';
12
-
13
11
  const arweaveTxIdRegex = /^[a-zA-Z0-9-_]{43}$/;
14
12
 
15
13
  const argv = yargs(hideBin(process.argv))
14
+ .version('2.1.0')
15
+ .help()
16
+ .usage('Usage: $0 --arns-name <name> [options]')
17
+ .example('$0 --arns-name my-app', 'Deploy to my-app.arweave.dev')
18
+ .example('$0 --arns-name my-app --undername staging', 'Deploy to staging.my-app.arweave.dev')
16
19
  .option('ario-process', {
17
20
  alias: 'p',
18
21
  type: 'string',
19
22
  description: 'The ARIO process to use',
20
23
  demandOption: true,
21
- default: ARIO_MAINNET_PROCESS_ID
24
+ default: ARIO_MAINNET_PROCESS_ID,
22
25
  })
23
26
  .option('arns-name', {
24
27
  alias: 'n',
@@ -35,25 +38,45 @@ const argv = yargs(hideBin(process.argv))
35
38
  .option('deploy-file', {
36
39
  alias: 'f',
37
40
  type: 'string',
38
- description: 'File to deploy.'
41
+ description: 'File to deploy.',
39
42
  })
40
43
  .option('ttl-seconds', {
41
44
  alias: 't',
42
45
  type: 'number',
43
46
  description: 'ArNS TTL Seconds',
44
- default: 3600
47
+ default: 3600,
45
48
  })
46
49
  .option('undername', {
47
50
  alias: 'u',
48
51
  type: 'string',
49
52
  description: 'ANT undername to update.',
50
53
  default: '@',
54
+ })
55
+ .option('sig-type', {
56
+ alias: 's',
57
+ type: 'string',
58
+ description: 'The type of signer to be used for deployment.',
59
+ choices: [
60
+ 'arweave',
61
+ 'ethereum',
62
+ 'polygon',
63
+ // 'solana',
64
+ 'kyve',
65
+ ],
66
+ default: 'arweave',
67
+ })
68
+ .check((argv) => {
69
+ if (argv.ttl < 60 || argv.ttl > 86400) {
70
+ throw new Error('TTL must be between 60 seconds (1 minute) and 86400 seconds (1 day)');
71
+ }
72
+ return true;
51
73
  }).argv;
52
74
 
53
75
  const DEPLOY_KEY = process.env.DEPLOY_KEY;
54
- const ARNS_NAME = argv.arnsName;
55
- const TTL_SECONDS = argv.ttlSeconds;
56
- let ARIO_PROCESS = argv.arioProcess;
76
+ const ARNS_NAME = argv['arns-name'];
77
+ let ARIO_PROCESS = argv['ario-process'];
78
+ const TTL_SECONDS = argv['ttl-seconds'];
79
+
57
80
  if (ARIO_PROCESS === 'mainnet') {
58
81
  ARIO_PROCESS = ARIO_MAINNET_PROCESS_ID;
59
82
  } else if (ARIO_PROCESS === 'testnet') {
@@ -84,8 +107,7 @@ if (ARIO_PROCESS === 'mainnet') {
84
107
  if (argv.deployFile && !fs.existsSync(argv.deployFile)) {
85
108
  console.error(`deploy-file [${argv.deployFolder}] does not exist`);
86
109
  process.exit(1);
87
- }
88
- else {
110
+ } else {
89
111
  if (!fs.existsSync(argv.deployFolder)) {
90
112
  console.error(`deploy-folder [${argv.deployFolder}] does not exist`);
91
113
  process.exit(1);
@@ -97,15 +119,14 @@ if (ARIO_PROCESS === 'mainnet') {
97
119
  process.exit(1);
98
120
  }
99
121
 
100
- const jwk = JSON.parse(Buffer.from(DEPLOY_KEY, 'base64').toString('utf-8'));
101
122
  const ario = ARIO.init({
102
123
  process: new AOProcess({
103
124
  processId: ARIO_PROCESS,
104
125
  ao: connect({
105
126
  MODE: 'legacy',
106
- CU_URL: 'https://cu.ardrive.io'
107
- })
108
- })
127
+ CU_URL: 'https://cu.ardrive.io',
128
+ }),
129
+ }),
109
130
  });
110
131
 
111
132
  const arnsNameRecord = await ario.getArNSRecord({ name: ARNS_NAME }).catch((e) => {
@@ -114,17 +135,88 @@ if (ARIO_PROCESS === 'mainnet') {
114
135
  });
115
136
 
116
137
  try {
117
- let txId;
118
- if (argv.deployFile) {
119
- const turbo = TurboFactory.authenticated({ privateKey: jwk });
120
- txId = (await uploadFile(argv.deployFile, turbo)).id;
138
+ let signer;
139
+ let token;
140
+
141
+ // Creates the proper signer based on the sig-type value
142
+ switch (argv['sig-type']) {
143
+ case 'ethereum':
144
+ signer = new EthereumSigner(DEPLOY_KEY);
145
+ token = 'ethereum';
146
+ break;
147
+ case 'polygon':
148
+ signer = new EthereumSigner(DEPLOY_KEY);
149
+ token = 'pol';
150
+ break;
151
+ case 'arweave':
152
+ const jwk = JSON.parse(Buffer.from(DEPLOY_KEY, 'base64').toString('utf-8'));
153
+ signer = new ArweaveSigner(jwk);
154
+ token = 'arweave';
155
+ break;
156
+ case 'kyve':
157
+ signer = new EthereumSigner(DEPLOY_KEY);
158
+ token = 'kyve';
159
+ break;
160
+ default:
161
+ throw new Error(
162
+ `Invalid sig-type provided: ${argv['sig-type']}. Allowed values are 'arweave', 'ethereum', 'polygon', or 'kyve'.`
163
+ );
121
164
  }
122
- else {
123
- txId = await uploadDirectory(argv, jwk);
165
+
166
+ const turbo = TurboFactory.authenticated({
167
+ signer: signer,
168
+ token: token,
169
+ });
170
+
171
+ let uploadResult;
172
+ let txOrManifestId;
173
+ if (argv['deploy-file']) {
174
+ // Detect MIME type for the file
175
+ const mimeType = mime.lookup(argv['deploy-file']) || 'application/octet-stream';
176
+
177
+ uploadResult = await turbo.uploadFile({
178
+ file: argv['deploy-file'],
179
+ dataItemOpts: {
180
+ tags: [
181
+ {
182
+ name: 'App-Name',
183
+ value: 'Permaweb-Deploy',
184
+ },
185
+ // prevents identical transaction Ids from eth wallets
186
+ {
187
+ name: 'anchor',
188
+ value: new Date().toISOString(),
189
+ },
190
+ {
191
+ name: 'Content-Type',
192
+ value: mimeType,
193
+ },
194
+ ],
195
+ },
196
+ });
197
+ txOrManifestId = uploadResult.id;
198
+ } else {
199
+ uploadResult = await turbo.uploadFolder({
200
+ folderPath: argv['deploy-folder'],
201
+ dataItemOpts: {
202
+ tags: [
203
+ {
204
+ name: 'App-Name',
205
+ value: 'Permaweb-Deploy',
206
+ },
207
+ // prevents identical transaction Ids from eth wallets
208
+ {
209
+ name: 'anchor',
210
+ value: new Date().toISOString(),
211
+ },
212
+ ],
213
+ },
214
+ });
215
+ txOrManifestId = uploadResult.manifestResponse.id;
124
216
  }
125
217
 
126
218
  console.log('-------------------- DEPLOY DETAILS --------------------');
127
- console.log(`Tx ID: ${txId}`);
219
+ console.log(`Tx ID: ${txOrManifestId}`);
128
220
  console.log(`ArNS Name: ${ARNS_NAME}`);
129
221
  console.log(`Undername: ${argv.undername}`);
130
222
  console.log(`ANT: ${arnsNameRecord.processId}`);
@@ -132,15 +224,14 @@ if (ARIO_PROCESS === 'mainnet') {
132
224
  console.log(`TTL Seconds: ${TTL_SECONDS}`);
133
225
  console.log('--------------------------------------------------------');
134
226
 
135
- const signer = new ArweaveSigner(jwk);
136
227
  const ant = ANT.init({ processId: arnsNameRecord.processId, signer });
137
228
 
138
229
  // Update the ANT record (assumes the JWK is a controller or owner)
139
230
  await ant.setRecord(
140
231
  {
141
232
  undername: argv.undername,
142
- transactionId: txId,
143
- ttlSeconds: TTL_SECONDS,
233
+ transactionId: txOrManifestId,
234
+ ttlSeconds: argv['ttl-seconds'],
144
235
  },
145
236
  {
146
237
  tags: [
@@ -148,15 +239,21 @@ if (ARIO_PROCESS === 'mainnet') {
148
239
  name: 'App-Name',
149
240
  value: 'Permaweb-Deploy',
150
241
  },
151
- ...(process.env.GITHUB_SHA ? [{
152
- name: 'GIT-HASH',
153
- value: process.env.GITHUB_SHA,
154
- }] : []),
155
- ]
242
+ ...(process.env.GITHUB_SHA
243
+ ? [
244
+ {
245
+ name: 'GIT-HASH',
246
+ value: process.env.GITHUB_SHA,
247
+ },
248
+ ]
249
+ : []),
250
+ ],
156
251
  }
157
252
  );
158
253
 
159
- console.log(`Deployed TxId [${txId}] to name [${ARNS_NAME}] for ANT [${arnsNameRecord.processId}] using undername [${argv.undername}]`);
254
+ console.log(
255
+ `Deployed TxId [${txOrManifestId}] to name [${ARNS_NAME}] for ANT [${arnsNameRecord.processId}] using undername [${argv.undername}]`
256
+ );
160
257
  } catch (e) {
161
258
  console.error('Deployment failed:', e);
162
259
  process.exit(1); // Exit with error code
@@ -1,121 +0,0 @@
1
- import fs from 'fs';
2
- import mime from 'mime-types';
3
- import path from 'path';
4
- import { Readable } from 'stream';
5
-
6
- import { TurboFactory } from '@ardrive/turbo-sdk';
7
-
8
- // Gets MIME types for each file to tag the upload
9
- function getContentType(filePath) {
10
- const res = mime.lookup(filePath);
11
- return res || 'application/octet-stream';
12
- }
13
-
14
- export async function uploadFile(path, turbo) {
15
- console.log(`Uploading file: ${path}...`);
16
- try {
17
- const fileSize = fs.statSync(path).size;
18
- const contentType = getContentType(path);
19
- const uploadResult = await turbo.uploadFile({
20
- fileStreamFactory: () => fs.createReadStream(path),
21
- fileSizeFactory: () => fileSize,
22
- signal: AbortSignal.timeout(10_000), // Cancel the upload after 10 seconds
23
- dataItemOpts: {
24
- tags: [
25
- { name: 'Content-Type', value: contentType },
26
- { name: 'App-Name', value: 'Permaweb-Deploy' },
27
- ],
28
- },
29
- });
30
-
31
- console.log(`Uploaded ${path} with id:`, uploadResult.id);
32
-
33
- return uploadResult;
34
- } catch (err) {
35
- console.error(`Error uploading file ${path}:`, err);
36
- }
37
- }
38
-
39
- export async function uploadDirectory(argv, jwk) {
40
- const turbo = TurboFactory.authenticated({ privateKey: jwk });
41
-
42
- const deployFolder = argv.deployFolder;
43
-
44
- // Uses Arweave manifest version 0.2.0, which supports fallbacks
45
- let newManifest = {
46
- manifest: 'arweave/paths',
47
- version: '0.2.0',
48
- index: { path: 'index.html' },
49
- fallback: {},
50
- paths: {},
51
- };
52
-
53
- async function processFiles(dir) {
54
- const files = fs.readdirSync(dir);
55
-
56
- for (const file of files) {
57
- try {
58
- const filePath = path.join(dir, file);
59
- const relativePath = path.relative(deployFolder, filePath);
60
-
61
- if (fs.statSync(filePath).isDirectory()) {
62
- // Recursively process all files in a directory
63
- await processFiles(filePath);
64
- } else {
65
- const uploadResult = await uploadFile(filePath, turbo);
66
-
67
- // Adds uploaded file txId to the new manifest json
68
- newManifest.paths[relativePath] = { id: uploadResult.id };
69
-
70
- if (file === '404.html') {
71
- // Sets manifest fallback to 404.html if found
72
- newManifest.fallback.id = uploadResult.id;
73
- }
74
- }
75
- } catch (err) {
76
- console.error('ERROR:', err);
77
- }
78
- }
79
- }
80
-
81
- async function uploadManifest(manifest) {
82
- try {
83
- const manifestString = JSON.stringify(manifest);
84
- const uploadResult = await turbo.uploadFile({
85
- fileStreamFactory: () => Readable.from(Buffer.from(manifestString)),
86
- fileSizeFactory: () => Buffer.byteLength(manifestString),
87
- signal: AbortSignal.timeout(10_000),
88
- dataItemOpts: {
89
- tags: [
90
- {
91
- name: 'Content-Type',
92
- value: 'application/x.arweave-manifest+json',
93
- },
94
- {
95
- name: 'App-Name',
96
- value: 'Permaweb-Deploy',
97
- },
98
- ],
99
- },
100
- });
101
- return uploadResult.id;
102
- } catch (error) {
103
- console.error('Error uploading manifest:', error);
104
- return null;
105
- }
106
- }
107
-
108
- // Starts processing files in the selected directory
109
- await processFiles(deployFolder);
110
-
111
- if (!newManifest.fallback.id) {
112
- // If no 404.html file is found, manifest fallback is set to the txId of index.html
113
- newManifest.fallback.id = newManifest.paths['index.html'].id;
114
- }
115
-
116
- const manifestId = await uploadManifest(newManifest);
117
- if (manifestId) {
118
- console.log(`Manifest uploaded with Id: ${manifestId}`);
119
- return manifestId;
120
- }
121
- }