@getxflow/cli 0.22.0 → 0.23.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/dist/commands/sources.js +32 -24
- package/dist/help.js +3 -1
- package/dist/tree.js +12 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +4 -2
package/dist/commands/sources.js
CHANGED
|
@@ -75,21 +75,27 @@ async function pushSources(root, config, client, options) {
|
|
|
75
75
|
const tree = prepareTree(root, config);
|
|
76
76
|
const state = (0, config_1.readState)(root);
|
|
77
77
|
const server = await latestRevision(client, config.projectId);
|
|
78
|
-
if (server
|
|
79
|
-
(0,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
if (server) {
|
|
79
|
+
const relation = (0, tree_1.syncState)(tree.hash, server.tree_hash, state.treeHash);
|
|
80
|
+
if (relation === 'matches') {
|
|
81
|
+
(0, config_1.writeState)(root, {
|
|
82
|
+
treeHash: server.tree_hash,
|
|
83
|
+
organizationId: client.organizationId ?? undefined,
|
|
84
|
+
});
|
|
85
|
+
(0, ui_1.ok)(`No changes, revision ${server.revision}`);
|
|
86
|
+
return { revision: server.revision, status: 'unchanged' };
|
|
87
|
+
}
|
|
88
|
+
// Asked before the upload, so a doomed archive never leaves the machine.
|
|
89
|
+
if (relation === 'diverged' && !options.force) {
|
|
90
|
+
throw new errors_1.CliError(`The server holds revision ${server.revision}, and this folder does not stand on it`, 'Someone deployed before you, or this folder worked on a different project. ' +
|
|
91
|
+
'Fetch the server copy alongside and compare: xflow pull --into ./server-copy');
|
|
92
|
+
}
|
|
93
|
+
if (options.force) {
|
|
94
|
+
await confirmForce(client, config.projectId, server, tree.files);
|
|
95
|
+
}
|
|
96
|
+
else if (relation === 'unrecorded') {
|
|
97
|
+
(0, ui_1.warn)(`This folder has no record of the server revision: the code goes up on top of revision ${server.revision}`);
|
|
98
|
+
}
|
|
93
99
|
}
|
|
94
100
|
(0, ui_1.step)(`Sending ${tree.files.length} files (${(0, ui_1.formatBytes)(tree.archive.length)})`);
|
|
95
101
|
const headers = {};
|
|
@@ -161,13 +167,10 @@ async function pull(args) {
|
|
|
161
167
|
'xflow pull --into ./server-copy, or overwrite the folder completely: xflow pull --force');
|
|
162
168
|
}
|
|
163
169
|
const { info, files } = await downloadRevision(client, config.projectId, target, (0, args_1.flagNumber)(args, 'revision'));
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
organizationId: client.organizationId ?? undefined,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
170
|
+
(0, config_1.writeState)(target, {
|
|
171
|
+
treeHash: info.tree_hash,
|
|
172
|
+
organizationId: client.organizationId ?? undefined,
|
|
173
|
+
});
|
|
171
174
|
(0, ui_1.ok)(`Revision ${info.revision}: ${files} files in ${target}`);
|
|
172
175
|
}
|
|
173
176
|
async function status() {
|
|
@@ -191,13 +194,18 @@ async function status() {
|
|
|
191
194
|
}
|
|
192
195
|
else {
|
|
193
196
|
(0, ui_1.out)(`On the server: revision ${server.revision}, ${server.file_count} files, ${(0, ui_1.formatAge)(server.created_at)}`);
|
|
194
|
-
|
|
197
|
+
const relation = (0, tree_1.syncState)(tree.hash, server.tree_hash, state.treeHash);
|
|
198
|
+
if (relation === 'matches') {
|
|
195
199
|
(0, ui_1.out)('State: matches the server');
|
|
196
200
|
}
|
|
197
|
-
else if (
|
|
201
|
+
else if (relation === 'diverged') {
|
|
198
202
|
(0, ui_1.out)(`State: ${(0, ui_1.bold)('diverged')}, the server holds a version this folder has not seen`);
|
|
199
203
|
(0, ui_1.note)((0, ui_1.dim)(' Fetch the server copy alongside: xflow pull --into ./server-copy'));
|
|
200
204
|
}
|
|
205
|
+
else if (relation === 'unrecorded') {
|
|
206
|
+
(0, ui_1.out)(`State: ${(0, ui_1.bold)('differs')}, this folder has no record of the server revision`);
|
|
207
|
+
(0, ui_1.note)((0, ui_1.dim)(` xflow deploy sends this code on top of revision ${server.revision}; to compare first: xflow pull --into ./server-copy`));
|
|
208
|
+
}
|
|
201
209
|
else {
|
|
202
210
|
(0, ui_1.out)(`State: ${(0, ui_1.bold)('local changes')}`);
|
|
203
211
|
(0, ui_1.note)((0, ui_1.dim)(' Send and build them: xflow deploy'));
|
package/dist/help.js
CHANGED
|
@@ -580,7 +580,9 @@ ignore field of xflow.json.
|
|
|
580
580
|
If the server holds a revision newer than the one you worked from, the sources are
|
|
581
581
|
rejected and the build does not start. That means somebody deployed before you: fetch
|
|
582
582
|
their changes alongside (${(0, ui_1.bold)('xflow pull --into ./server-copy')}), merge them in git on
|
|
583
|
-
your side, and retry.
|
|
583
|
+
your side, and retry. A folder with no record of the server revision (a fresh clone, a CI
|
|
584
|
+
checkout) is not rejected: its first deploy goes on top of the current revision and says
|
|
585
|
+
so, the check applies from the next one. ${(0, ui_1.bold)('--force')} overwrites the server revision, and before that the
|
|
584
586
|
CLI shows whose work you are about to destroy and asks for confirmation by typing the
|
|
585
587
|
project name. In a non-interactive run (CI, an agent) there is no way to confirm: a
|
|
586
588
|
version conflict has to fail the build rather than silently destroy somebody else's work.
|
package/dist/tree.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.isSkillCopy = isSkillCopy;
|
|
4
4
|
exports.loadIgnoreRules = loadIgnoreRules;
|
|
5
5
|
exports.collectFiles = collectFiles;
|
|
6
|
+
exports.syncState = syncState;
|
|
6
7
|
exports.treeHash = treeHash;
|
|
7
8
|
exports.heaviest = heaviest;
|
|
8
9
|
const node_crypto_1 = require("node:crypto");
|
|
@@ -130,6 +131,17 @@ function collectFiles(root, rules) {
|
|
|
130
131
|
files.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
131
132
|
return { files, skippedLinks };
|
|
132
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* How the folder stands against the server tree. A folder with no record (a fresh
|
|
136
|
+
* clone, a CI checkout) has not diverged: nothing is known about what it started from.
|
|
137
|
+
*/
|
|
138
|
+
function syncState(local, server, remembered) {
|
|
139
|
+
if (local === server)
|
|
140
|
+
return 'matches';
|
|
141
|
+
if (remembered === undefined)
|
|
142
|
+
return 'unrecorded';
|
|
143
|
+
return remembered === server ? 'local-changes' : 'diverged';
|
|
144
|
+
}
|
|
133
145
|
/** Computed identically on the server. */
|
|
134
146
|
function treeHash(files) {
|
|
135
147
|
const parts = files.map((file) => `${file.path}\0${(0, node_crypto_1.createHash)('sha256').update(file.content).digest('hex')}`);
|
package/dist/version.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DEFAULT_API_URL = exports.CLI_VERSION = void 0;
|
|
4
4
|
/** Keep in sync with cli/package.json. */
|
|
5
|
-
exports.CLI_VERSION = '0.
|
|
5
|
+
exports.CLI_VERSION = '0.23.0';
|
|
6
6
|
/** Overridden by XFLOW_API_URL or the `api` field in xflow.json. */
|
|
7
7
|
exports.DEFAULT_API_URL = 'https://app.getxflow.com';
|
package/package.json
CHANGED
package/skills/xflow/SKILL.md
CHANGED
|
@@ -22,7 +22,7 @@ contains the fix.
|
|
|
22
22
|
|
|
23
23
|
## Keeping these instructions current
|
|
24
24
|
|
|
25
|
-
These instructions ship with xflow CLI 0.
|
|
25
|
+
These instructions ship with xflow CLI 0.23.0, so the copy you are reading can be older than
|
|
26
26
|
the CLI answering your commands.
|
|
27
27
|
|
|
28
28
|
When the CLI says this file does not match it, run `xflow skills --refresh` and then **read
|
|
@@ -108,7 +108,9 @@ one exception is the very first version of a project, which publishes automatica
|
|
|
108
108
|
`xflow status` says how the local copy differs from the server revision. A deploy rejected
|
|
109
109
|
before the build starts means the server holds a version this folder has never seen: somebody
|
|
110
110
|
deployed first, or the folder was linked elsewhere. Fetch it next to yours (`xflow pull --into
|
|
111
|
-
./server-copy`), merge in git, deploy again. `--force` destroys that work.
|
|
111
|
+
./server-copy`), merge in git, deploy again. `--force` destroys that work. A folder with no
|
|
112
|
+
record of the server revision (a fresh clone, a CI checkout) is not rejected: its first deploy
|
|
113
|
+
goes on top of the current revision and says so, the check applies from the next one.
|
|
112
114
|
|
|
113
115
|
**Only pages are versioned.** Cloud functions and the database are one per project: they
|
|
114
116
|
are not versioned, and dev and live share them. So `xflow deploy` changes the running
|