permaweb-deploy 2.2.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,8 +1,9 @@
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
7
  - **Turbo SDK Integration:** Uses Turbo SDK for fast, reliable file uploads to Arweave
7
8
  - **Arweave Manifest v0.2.0:** Creates manifests with fallback support for SPAs
8
9
  - **ArNS Updates:** Updates ArNS records via ANT with new transaction IDs and metadata
@@ -10,6 +11,7 @@ Inspired by the [cookbook github action deployment guide](https://cookbook.arwea
10
11
  - **Git Hash Tagging:** Automatically tags deployments with Git commit hashes
11
12
  - **404 Fallback Detection:** Automatically detects and sets 404.html as fallback
12
13
  - **Network Support:** Supports mainnet, testnet, and custom ARIO process IDs
14
+ - **Flexible Deployment:** Supports deploying a folder or a single file
13
15
 
14
16
  ### Installation
15
17
 
@@ -20,39 +22,30 @@ npm install permaweb-deploy
20
22
  ```
21
23
 
22
24
  For development use:
25
+
23
26
  ```bash
24
27
  npm install permaweb-deploy --save-dev
25
28
  ```
26
29
 
27
30
  For Yarn users:
31
+
28
32
  ```bash
29
33
  yarn add permaweb-deploy --dev --ignore-engines
30
34
  ```
31
35
 
32
36
  ### Prerequisites
33
37
 
34
- Before using `permaweb-deploy`, you must:
35
- 1. **Arweave Wallet:** Have an Arweave wallet with Turbo Credits for uploading
36
- 2. **ArNS Name:** Own or control an ArNS name (which has an associated ANT process)
37
- 3. **Wallet Encoding:** Encode your Arweave wallet key in base64 format:
38
+ 1. **For Arweave signer (default):** Encode your Arweave wallet key in base64 format and set it as a GitHub secret:
39
+
38
40
  ```bash
39
41
  base64 -i wallet.json | pbcopy
40
42
  ```
41
- 4. **GitHub Secret:** Set the encoded wallet as a GitHub secret named `DEPLOY_KEY`
42
43
 
43
- ⚠️ **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`.
44
45
 
45
- ### CLI Options
46
- ```bash
47
- permaweb-deploy [options]
48
- ```
46
+ 3. Ensure that the secret name for the encoded wallet or private key is `DEPLOY_KEY`.
49
47
 
50
- | Option | Alias | Description | Default | Required |
51
- |--------|-------|-------------|---------|----------|
52
- | `--arns-name` | `-n` | ArNS name for deployment | - | ✅ |
53
- | `--ario-process` | `-p` | ARIO process ID or "mainnet"/"testnet" | `mainnet` | ❌ |
54
- | `--deploy-folder` | `-d` | Folder to deploy | `./dist` | ❌ |
55
- | `--undername` | `-u` | ANT undername to update | `@` | ❌ |
48
+ ⚠️ **Important:** Use a dedicated wallet for deployments to minimize security risks. Ensure your wallet has sufficient Turbo Credits for uploads.
56
49
 
57
50
  ### Usage
58
51
 
@@ -61,67 +54,107 @@ To deploy your application, ensure you have a build script and a deployment scri
61
54
  ```json
62
55
  "scripts": {
63
56
  "build": "your-build-command",
64
- "deploy-main": "npm run build && permaweb-deploy --arns-name <ARNS_NAME>"
57
+ "deploy": "npm run build && permaweb-deploy --arns-name <ARNS_NAME>"
65
58
  }
66
59
  ```
67
60
 
68
- **Example with custom options:**
69
- ```bash
70
- permaweb-deploy --arns-name "your-arns-name" --deploy-folder "./build" --undername "app"
61
+ Replace `<ARNS_NAME>` with your ArNS name. To deploy to an undername, add `--undername <UNDERNAME>`.
62
+
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
99
+ ```
100
+
101
+ Deploy with a custom TTL:
102
+
103
+ ```sh
104
+ DEPLOY_KEY=$(base64 -i wallet.json) npx permaweb-deploy --arns-name my-app --ttl-seconds 7200
71
105
  ```
72
106
 
73
- Replace `<ARNS_NAME>` with your ArNS name. You can also specify testnet, mainnet, and custom process IDs for the ARIO process to use.
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
74
114
 
75
- **Mainnet (default) config:**
76
115
  ```json
77
116
  "scripts": {
78
- "build": "your-build-command",
79
- "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>"
80
119
  }
81
120
  ```
82
121
 
83
- **Testnet config:**
122
+ #### ARIO Process Examples
123
+
124
+ **Mainnet (default):**
125
+
84
126
  ```json
85
127
  "scripts": {
86
- "build": "your-build-command",
87
- "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>"
88
129
  }
89
130
  ```
90
131
 
91
- **Custom process ID config:**
132
+ **Testnet:**
133
+
92
134
  ```json
93
135
  "scripts": {
94
- "build": "your-build-command",
95
- "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"
96
137
  }
97
138
  ```
98
139
 
99
- ### Manual CLI Deployment
100
- ```bash
101
- DEPLOY_KEY=$(base64 -i wallet.json) npx permaweb-deploy --arns-name <ARNS_NAME>
102
- ```
140
+ **Custom process ID:**
103
141
 
104
- ### Technical Details
105
- - **Upload Service:** Uses Turbo SDK for fast, reliable file uploads to Arweave
106
- - **Manifest Format:** Creates Arweave manifests using version 0.2.0 specification
107
- - **Fallback Support:** Automatically detects `404.html` and sets it as fallback, otherwise uses `index.html`
108
- - **Upload Timeout:** 10-second timeout per file upload for reliability
109
- - **ArNS Record TTL:** Sets 3600 seconds (1 hour) TTL for ArNS records via ANT
110
- - **Deployment Tags:** Automatically adds `App-Name: Permaweb-Deploy` and Git hash tags
111
- - **Network Support:** Supports mainnet, testnet, and custom ARIO process IDs
142
+ ```json
143
+ "scripts": {
144
+ "deploy-custom": "npm run build && permaweb-deploy --arns-name <ARNS_NAME> --ario-process <PROCESS_ID>"
145
+ }
146
+ ```
112
147
 
113
148
  ### GitHub Actions Workflow
114
149
 
115
150
  To automate the deployment, set up a GitHub Actions workflow as follows:
116
151
 
117
152
  ```yaml
118
- name: publish
119
-
153
+ name: Deploy to Permaweb
120
154
  on:
121
155
  push:
122
156
  branches:
123
- - 'main'
124
-
157
+ - main
125
158
  jobs:
126
159
  publish:
127
160
  runs-on: ubuntu-latest
@@ -131,20 +164,23 @@ jobs:
131
164
  with:
132
165
  node-version: 20.x
133
166
  - run: npm install
134
- - run: npm run deploy-main
167
+ - run: npm run deploy
135
168
  env:
136
169
  DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
137
170
  ```
138
171
 
172
+
139
173
  ### Security & Best Practices
174
+
140
175
  - **Dedicated Wallet:** Always use a dedicated wallet for deployments to minimize security risks
141
- - **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
142
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
143
178
  - **Turbo Credits:** Ensure your wallet has sufficient Turbo Credits before deployment
144
179
  - **Secret Management:** Keep your `DEPLOY_KEY` secret secure and never commit it to your repository
145
180
  - **Build Security:** Always check your build for exposed environmental secrets before deployment, as data on Arweave is permanent
146
181
 
147
182
  ### Troubleshooting
183
+
148
184
  - **Error: "ARNS_NAME not configured":** Ensure you're passing the `--arns-name` flag with a valid ArNS name
149
185
  - **Error: "DEPLOY_KEY not configured":** Verify your base64 encoded wallet is set as the `DEPLOY_KEY` environment variable
150
186
  - **Error: "deploy-folder does not exist":** Check that your build folder exists and the path is correct
@@ -154,8 +190,8 @@ jobs:
154
190
  - **Insufficient Turbo Credits:** Ensure your wallet has enough Turbo Credits for the deployment
155
191
 
156
192
  ### Dependencies
193
+
157
194
  - **@ar.io/sdk:** - For ANT operations and ArNS management
158
195
  - **@ardrive/turbo-sdk:** - For fast file uploads to Arweave
159
196
  - **@permaweb/aoconnect:** - For AO network connectivity
160
- - **mime-types:** - For automatic content type detection
161
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',
@@ -40,22 +40,41 @@ var argv = (0, _yargs["default"])((0, _helpers.hideBin)(process.argv)).option('a
40
40
  alias: 'f',
41
41
  type: 'string',
42
42
  description: 'File to deploy.'
43
+ }).option('ttl-seconds', {
44
+ alias: 't',
45
+ type: 'number',
46
+ description: 'ArNS TTL Seconds',
47
+ "default": 3600
43
48
  }).option('undername', {
44
49
  alias: 'u',
45
50
  type: 'string',
46
51
  description: 'ANT undername to update.',
47
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;
48
66
  }).argv;
49
67
  var DEPLOY_KEY = process.env.DEPLOY_KEY;
50
- var ARNS_NAME = argv.arnsName;
51
- 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'];
52
71
  if (ARIO_PROCESS === 'mainnet') {
53
72
  ARIO_PROCESS = _sdk.ARIO_MAINNET_PROCESS_ID;
54
73
  } else if (ARIO_PROCESS === 'testnet') {
55
74
  ARIO_PROCESS = _sdk.ARIO_TESTNET_PROCESS_ID;
56
75
  }
57
76
  _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
58
- var jwk, ario, arnsNameRecord, txId, turbo, signer, ant, _t;
77
+ var ario, arnsNameRecord, signer, token, jwk, turbo, uploadResult, txOrManifestId, mimeType, ant, _t, _t2;
59
78
  return _regenerator().w(function (_context) {
60
79
  while (1) switch (_context.n) {
61
80
  case 0:
@@ -71,6 +90,10 @@ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
71
90
  console.error('ARNS_NAME not configured');
72
91
  process.exit(1);
73
92
  }
93
+ if (!Number.isFinite(TTL_SECONDS) || TTL_SECONDS < 60 || TTL_SECONDS > 86400) {
94
+ console.error('TTL_SECONDS must be a number between 60 and 86400 seconds');
95
+ process.exit(1);
96
+ }
74
97
  if (argv.deployFile && !_fs["default"].existsSync(argv.deployFile)) {
75
98
  console.error("deploy-file [".concat(argv.deployFolder, "] does not exist"));
76
99
  process.exit(1);
@@ -84,7 +107,6 @@ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
84
107
  console.error('undername must be set');
85
108
  process.exit(1);
86
109
  }
87
- jwk = JSON.parse(Buffer.from(DEPLOY_KEY, 'base64').toString('utf-8'));
88
110
  ario = _sdk.ARIO.init({
89
111
  process: new _sdk.AOProcess({
90
112
  processId: ARIO_PROCESS,
@@ -104,35 +126,99 @@ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
104
126
  case 1:
105
127
  arnsNameRecord = _context.v;
106
128
  _context.p = 2;
107
- if (!argv.deployFile) {
108
- _context.n = 4;
109
- break;
110
- }
111
- turbo = _turboSdk.TurboFactory.authenticated({
112
- privateKey: jwk
113
- });
114
- _context.n = 3;
115
- return (0, _turbo.uploadFile)(argv.deployFile, turbo);
116
- case 3:
117
- txId = _context.v.id;
118
- _context.n = 6;
129
+ _t = argv['sig-type'];
130
+ _context.n = _t === 'ethereum' ? 3 : _t === 'polygon' ? 4 : _t === 'arweave' ? 5 : _t === 'kyve' ? 6 : 7;
119
131
  break;
132
+ case 3:
133
+ signer = new _turboSdk.EthereumSigner(DEPLOY_KEY);
134
+ token = 'ethereum';
135
+ return _context.a(3, 8);
120
136
  case 4:
121
- _context.n = 5;
122
- return (0, _turbo.uploadDirectory)(argv, jwk);
137
+ signer = new _turboSdk.EthereumSigner(DEPLOY_KEY);
138
+ token = 'pol';
139
+ return _context.a(3, 8);
123
140
  case 5:
124
- txId = _context.v;
125
- case 6:
141
+ jwk = JSON.parse(Buffer.from(DEPLOY_KEY, 'base64').toString('utf-8'));
126
142
  signer = new _sdk.ArweaveSigner(jwk);
143
+ token = 'arweave';
144
+ return _context.a(3, 8);
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:
205
+ console.log('-------------------- DEPLOY DETAILS --------------------');
206
+ console.log("Tx ID: ".concat(txOrManifestId));
207
+ console.log("ArNS Name: ".concat(ARNS_NAME));
208
+ console.log("Undername: ".concat(argv.undername));
209
+ console.log("ANT: ".concat(arnsNameRecord.processId));
210
+ console.log("AR IO Process: ".concat(ARIO_PROCESS));
211
+ console.log("TTL Seconds: ".concat(TTL_SECONDS));
212
+ console.log('--------------------------------------------------------');
127
213
  ant = _sdk.ANT.init({
128
214
  processId: arnsNameRecord.processId,
129
215
  signer: signer
130
216
  }); // Update the ANT record (assumes the JWK is a controller or owner)
131
- _context.n = 7;
217
+ _context.n = 13;
132
218
  return ant.setRecord({
133
219
  undername: argv.undername,
134
- transactionId: txId,
135
- ttlSeconds: 3600
220
+ transactionId: txOrManifestId,
221
+ ttlSeconds: argv['ttl-seconds']
136
222
  }, {
137
223
  tags: [{
138
224
  name: 'App-Name',
@@ -142,17 +228,17 @@ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
142
228
  value: process.env.GITHUB_SHA
143
229
  }] : []))
144
230
  });
145
- case 7:
146
- console.log("Deployed TxId [".concat(txId, "] to name [").concat(ARNS_NAME, "] for ANT [").concat(arnsNameRecord.processId, "] using undername [").concat(argv.undername, "]"));
147
- _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;
148
234
  break;
149
- case 8:
150
- _context.p = 8;
151
- _t = _context.v;
152
- console.error('Deployment failed:', _t);
235
+ case 14:
236
+ _context.p = 14;
237
+ _t2 = _context.v;
238
+ console.error('Deployment failed:', _t2);
153
239
  process.exit(1); // Exit with error code
154
- case 9:
240
+ case 15:
155
241
  return _context.a(2);
156
242
  }
157
- }, _callee, null, [[2, 8]]);
243
+ }, _callee, null, [[2, 14]]);
158
244
  }))();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "permaweb-deploy",
3
- "version": "2.2.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,18 +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.',
42
+ })
43
+ .option('ttl-seconds', {
44
+ alias: 't',
45
+ type: 'number',
46
+ description: 'ArNS TTL Seconds',
47
+ default: 3600,
39
48
  })
40
49
  .option('undername', {
41
50
  alias: 'u',
42
51
  type: 'string',
43
52
  description: 'ANT undername to update.',
44
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;
45
73
  }).argv;
46
74
 
47
75
  const DEPLOY_KEY = process.env.DEPLOY_KEY;
48
- const ARNS_NAME = argv.arnsName;
49
- 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
+
50
80
  if (ARIO_PROCESS === 'mainnet') {
51
81
  ARIO_PROCESS = ARIO_MAINNET_PROCESS_ID;
52
82
  } else if (ARIO_PROCESS === 'testnet') {
@@ -69,11 +99,15 @@ if (ARIO_PROCESS === 'mainnet') {
69
99
  process.exit(1);
70
100
  }
71
101
 
102
+ if (!Number.isFinite(TTL_SECONDS) || TTL_SECONDS < 60 || TTL_SECONDS > 86400) {
103
+ console.error('TTL_SECONDS must be a number between 60 and 86400 seconds');
104
+ process.exit(1);
105
+ }
106
+
72
107
  if (argv.deployFile && !fs.existsSync(argv.deployFile)) {
73
108
  console.error(`deploy-file [${argv.deployFolder}] does not exist`);
74
109
  process.exit(1);
75
- }
76
- else {
110
+ } else {
77
111
  if (!fs.existsSync(argv.deployFolder)) {
78
112
  console.error(`deploy-folder [${argv.deployFolder}] does not exist`);
79
113
  process.exit(1);
@@ -85,15 +119,14 @@ if (ARIO_PROCESS === 'mainnet') {
85
119
  process.exit(1);
86
120
  }
87
121
 
88
- const jwk = JSON.parse(Buffer.from(DEPLOY_KEY, 'base64').toString('utf-8'));
89
122
  const ario = ARIO.init({
90
123
  process: new AOProcess({
91
124
  processId: ARIO_PROCESS,
92
125
  ao: connect({
93
126
  MODE: 'legacy',
94
- CU_URL: 'https://cu.ardrive.io'
95
- })
96
- })
127
+ CU_URL: 'https://cu.ardrive.io',
128
+ }),
129
+ }),
97
130
  });
98
131
 
99
132
  const arnsNameRecord = await ario.getArNSRecord({ name: ARNS_NAME }).catch((e) => {
@@ -102,24 +135,103 @@ if (ARIO_PROCESS === 'mainnet') {
102
135
  });
103
136
 
104
137
  try {
105
- let txId;
106
- if (argv.deployFile) {
107
- const turbo = TurboFactory.authenticated({ privateKey: jwk });
108
- 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
+ );
109
164
  }
110
- else {
111
- 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;
112
216
  }
113
217
 
114
- const signer = new ArweaveSigner(jwk);
218
+ console.log('-------------------- DEPLOY DETAILS --------------------');
219
+ console.log(`Tx ID: ${txOrManifestId}`);
220
+ console.log(`ArNS Name: ${ARNS_NAME}`);
221
+ console.log(`Undername: ${argv.undername}`);
222
+ console.log(`ANT: ${arnsNameRecord.processId}`);
223
+ console.log(`AR IO Process: ${ARIO_PROCESS}`);
224
+ console.log(`TTL Seconds: ${TTL_SECONDS}`);
225
+ console.log('--------------------------------------------------------');
226
+
115
227
  const ant = ANT.init({ processId: arnsNameRecord.processId, signer });
116
228
 
117
229
  // Update the ANT record (assumes the JWK is a controller or owner)
118
230
  await ant.setRecord(
119
231
  {
120
232
  undername: argv.undername,
121
- transactionId: txId,
122
- ttlSeconds: 3600,
233
+ transactionId: txOrManifestId,
234
+ ttlSeconds: argv['ttl-seconds'],
123
235
  },
124
236
  {
125
237
  tags: [
@@ -127,15 +239,21 @@ if (ARIO_PROCESS === 'mainnet') {
127
239
  name: 'App-Name',
128
240
  value: 'Permaweb-Deploy',
129
241
  },
130
- ...(process.env.GITHUB_SHA ? [{
131
- name: 'GIT-HASH',
132
- value: process.env.GITHUB_SHA,
133
- }] : []),
134
- ]
242
+ ...(process.env.GITHUB_SHA
243
+ ? [
244
+ {
245
+ name: 'GIT-HASH',
246
+ value: process.env.GITHUB_SHA,
247
+ },
248
+ ]
249
+ : []),
250
+ ],
135
251
  }
136
252
  );
137
253
 
138
- 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
+ );
139
257
  } catch (e) {
140
258
  console.error('Deployment failed:', e);
141
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
- }