pagan-artifact 0.2.8 → 0.3.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/bin/art.js +15 -2
- package/branching/index.js +117 -46
- package/caches/index.js +112 -68
- package/changes/index.js +49 -15
- package/contributions/index.js +125 -95
- package/index.js +2 -2
- package/package.json +1 -1
- package/setup/index.js +197 -157
- package/utils/getStateByHash/index.js +61 -41
- package/workflow/index.js +199 -150
package/changes/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Module: Changes (v0.
|
|
3
|
+
* Module: Changes (v0.3.0)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
@@ -34,15 +34,17 @@ function log () {
|
|
|
34
34
|
|
|
35
35
|
let output = `Branch: ${branch}\n\n`;
|
|
36
36
|
|
|
37
|
-
// Display commits in reverse chronological order
|
|
38
|
-
|
|
39
37
|
for (let i = manifest.commits.length - 1; i >= 0; i--) {
|
|
40
38
|
const hash = manifest.commits[i];
|
|
41
|
-
const
|
|
39
|
+
const commitMasterPath = path.join(branchPath, `${hash}.json`);
|
|
40
|
+
|
|
41
|
+
if (fs.existsSync(commitMasterPath)) {
|
|
42
|
+
const commitData = JSON.parse(fs.readFileSync(commitMasterPath, 'utf8'));
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
output += `commit ${commitData.hash}\n`;
|
|
45
|
+
output += `Date: ${new Date(commitData.timestamp).toLocaleString()}\n`;
|
|
46
|
+
output += `\n ${commitData.message}\n\n`;
|
|
47
|
+
}
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
return output;
|
|
@@ -50,14 +52,19 @@ function log () {
|
|
|
50
52
|
|
|
51
53
|
/**
|
|
52
54
|
* Displays line-by-line differences between working directory and the last commit/stage.
|
|
53
|
-
* @returns {
|
|
55
|
+
* @returns {object} Formatted diff output and staged file list.
|
|
54
56
|
*/
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
function diff () {
|
|
57
59
|
const root = process.cwd();
|
|
58
60
|
const artPath = path.join(root, '.art');
|
|
59
|
-
const
|
|
61
|
+
const artJsonPath = path.join(artPath, 'art.json');
|
|
62
|
+
|
|
63
|
+
if (!fs.existsSync(artJsonPath)) {
|
|
64
|
+
throw new Error('No art repository found.');
|
|
65
|
+
}
|
|
60
66
|
|
|
67
|
+
const artJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
|
|
61
68
|
const activeBranch = artJson.active.branch;
|
|
62
69
|
const lastCommitHash = artJson.active.parent;
|
|
63
70
|
const lastCommitState = lastCommitHash ? getStateByHash(activeBranch, lastCommitHash) : {};
|
|
@@ -69,10 +76,13 @@ function log () {
|
|
|
69
76
|
|
|
70
77
|
for (const filePath of currentFiles) {
|
|
71
78
|
const fullPath = path.join(root, filePath);
|
|
72
|
-
const
|
|
79
|
+
const currentBuffer = fs.readFileSync(fullPath);
|
|
80
|
+
const isBinary = currentBuffer.includes(0);
|
|
81
|
+
|
|
82
|
+
const currentContent = isBinary ? null : currentBuffer.toString('utf8');
|
|
73
83
|
const previousContent = lastCommitState[filePath] || '';
|
|
74
84
|
|
|
75
|
-
if (currentContent !== previousContent) {
|
|
85
|
+
if (!isBinary && currentContent !== previousContent) {
|
|
76
86
|
let start = 0;
|
|
77
87
|
|
|
78
88
|
while (start < previousContent.length && start < currentContent.length && previousContent[start] === currentContent[start]) {
|
|
@@ -92,17 +102,41 @@ function log () {
|
|
|
92
102
|
deleted: previousContent.slice(start, oldEnd + 1),
|
|
93
103
|
added: currentContent.slice(start, newEnd + 1)
|
|
94
104
|
});
|
|
105
|
+
} else if (isBinary) {
|
|
106
|
+
const prevHash = lastCommitState[filePath] ? 'exists' : 'null';
|
|
107
|
+
|
|
108
|
+
if (prevHash === 'null') {
|
|
109
|
+
fileDiffs.push({ file: filePath, added: '<Binary Data>', deleted: '' });
|
|
110
|
+
}
|
|
95
111
|
}
|
|
96
112
|
}
|
|
97
113
|
|
|
98
|
-
const
|
|
99
|
-
const
|
|
114
|
+
const stageDir = path.join(artPath, 'stage');
|
|
115
|
+
const stageManifestPath = path.join(stageDir, 'manifest.json');
|
|
116
|
+
|
|
117
|
+
let staged = [];
|
|
118
|
+
|
|
119
|
+
if (fs.existsSync(stageManifestPath)) {
|
|
120
|
+
const manifest = JSON.parse(fs.readFileSync(stageManifestPath, 'utf8'));
|
|
121
|
+
const stagedFilesSet = new Set();
|
|
122
|
+
|
|
123
|
+
for (const partName of manifest.parts) {
|
|
124
|
+
const partPath = path.join(stageDir, partName);
|
|
125
|
+
|
|
126
|
+
if (fs.existsSync(partPath)) {
|
|
127
|
+
const partData = JSON.parse(fs.readFileSync(partPath, 'utf8'));
|
|
128
|
+
|
|
129
|
+
Object.keys(partData.changes).forEach(file => stagedFilesSet.add(file));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
staged = Array.from(stagedFilesSet);
|
|
133
|
+
}
|
|
100
134
|
|
|
101
135
|
return { fileDiffs, staged };
|
|
102
136
|
}
|
|
103
137
|
|
|
104
138
|
module.exports = {
|
|
105
|
-
__libraryVersion: '0.
|
|
139
|
+
__libraryVersion: '0.3.0',
|
|
106
140
|
__libraryAPIName: 'Changes',
|
|
107
141
|
log,
|
|
108
142
|
diff
|
package/contributions/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Module: Contributions (v0.
|
|
3
|
+
* Module: Contributions (v0.3.0)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
@@ -13,25 +13,19 @@ const ARTIFACT_HOST = pkg.artConfig.host || 'http://localhost:1337';
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Configures the single URL endpoint in art.json for synchronization.
|
|
16
|
-
* Supports full URLs or "handle/repo" slugs.
|
|
17
16
|
*/
|
|
18
17
|
|
|
19
18
|
function remote (input) {
|
|
20
19
|
const artPath = path.join(process.cwd(), '.art', 'art.json');
|
|
21
|
-
|
|
22
|
-
if (!fs.existsSync(artPath)) {
|
|
23
|
-
throw new Error('No art repository found.');
|
|
24
|
-
}
|
|
20
|
+
if (!fs.existsSync(artPath)) throw new Error('No art repository found.');
|
|
25
21
|
|
|
26
22
|
const artJson = JSON.parse(fs.readFileSync(artPath, 'utf8'));
|
|
27
23
|
|
|
28
24
|
if (input) {
|
|
29
25
|
let finalUrl = input;
|
|
30
|
-
|
|
31
26
|
if (input.includes('/') && !input.startsWith('http')) {
|
|
32
27
|
finalUrl = `${ARTIFACT_HOST}/${input}`;
|
|
33
28
|
}
|
|
34
|
-
|
|
35
29
|
artJson.remote = finalUrl;
|
|
36
30
|
fs.writeFileSync(artPath, JSON.stringify(artJson, null, 2));
|
|
37
31
|
}
|
|
@@ -40,16 +34,14 @@ function remote (input) {
|
|
|
40
34
|
}
|
|
41
35
|
|
|
42
36
|
/**
|
|
43
|
-
* Downloads JSON diff files from the remote server
|
|
37
|
+
* Downloads paginated JSON diff files from the remote server.
|
|
44
38
|
*/
|
|
45
39
|
|
|
46
40
|
async function fetchRemote () {
|
|
47
41
|
const artPath = path.join(process.cwd(), '.art');
|
|
48
42
|
const artJson = JSON.parse(fs.readFileSync(path.join(artPath, 'art.json'), 'utf8'));
|
|
49
43
|
|
|
50
|
-
if (!artJson.remote)
|
|
51
|
-
throw new Error('Remote URL not configured. Use "art remote <handle>/<repo>".');
|
|
52
|
-
}
|
|
44
|
+
if (!artJson.remote) throw new Error('Remote URL not configured.');
|
|
53
45
|
|
|
54
46
|
const branch = artJson.active.branch;
|
|
55
47
|
const token = artJson.configuration.personalAccessToken;
|
|
@@ -72,7 +64,6 @@ async function fetchRemote () {
|
|
|
72
64
|
handle,
|
|
73
65
|
repo,
|
|
74
66
|
branch,
|
|
75
|
-
|
|
76
67
|
...(token && { personalAccessToken: token })
|
|
77
68
|
})
|
|
78
69
|
});
|
|
@@ -80,38 +71,50 @@ async function fetchRemote () {
|
|
|
80
71
|
const remoteManifest = await response.json();
|
|
81
72
|
|
|
82
73
|
for (const commitHash of remoteManifest.commits) {
|
|
83
|
-
const
|
|
74
|
+
const commitMasterPath = path.join(remoteBranchPath, `${commitHash}.json`);
|
|
84
75
|
|
|
85
|
-
if (!fs.existsSync(
|
|
86
|
-
const
|
|
76
|
+
if (!fs.existsSync(commitMasterPath)) {
|
|
77
|
+
const commitRes = await fetch(`${ARTIFACT_HOST}/commit`, {
|
|
87
78
|
method: 'POST',
|
|
88
79
|
headers: { 'Content-Type': 'application/json' },
|
|
89
80
|
body: JSON.stringify({
|
|
90
|
-
handle,
|
|
91
|
-
repo,
|
|
92
|
-
branch,
|
|
93
|
-
hash: commitHash,
|
|
94
|
-
|
|
81
|
+
handle, repo, branch, hash: commitHash,
|
|
95
82
|
...(token && { personalAccessToken: token })
|
|
96
83
|
})
|
|
97
84
|
});
|
|
98
85
|
|
|
99
|
-
const
|
|
86
|
+
const commitMaster = await commitRes.json();
|
|
100
87
|
|
|
101
|
-
fs.writeFileSync(
|
|
88
|
+
fs.writeFileSync(commitMasterPath, JSON.stringify(commitMaster, null, 2));
|
|
89
|
+
|
|
90
|
+
for (const partName of commitMaster.parts) {
|
|
91
|
+
const partPath = path.join(remoteBranchPath, partName);
|
|
92
|
+
|
|
93
|
+
if (!fs.existsSync(partPath)) {
|
|
94
|
+
const partRes = await fetch(`${ARTIFACT_HOST}/commit/part`, {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
headers: { 'Content-Type': 'application/json' },
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
handle, repo, branch, partName,
|
|
99
|
+
...(token && { personalAccessToken: token })
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const partData = await partRes.json();
|
|
104
|
+
|
|
105
|
+
fs.writeFileSync(partPath, JSON.stringify(partData, null, 2));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
102
108
|
}
|
|
103
109
|
}
|
|
104
110
|
|
|
105
|
-
fs.writeFileSync(
|
|
106
|
-
path.join(remoteBranchPath, 'manifest.json'),
|
|
107
|
-
JSON.stringify(remoteManifest, null, 2)
|
|
108
|
-
);
|
|
111
|
+
fs.writeFileSync(path.join(remoteBranchPath, 'manifest.json'), JSON.stringify(remoteManifest, null, 2));
|
|
109
112
|
|
|
110
113
|
return `Fetched remote history for branch: ${branch}`;
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
/**
|
|
114
|
-
*
|
|
117
|
+
* Applies remote paginated commits to local branch.
|
|
115
118
|
*/
|
|
116
119
|
|
|
117
120
|
async function pull () {
|
|
@@ -121,11 +124,11 @@ async function pull () {
|
|
|
121
124
|
|
|
122
125
|
await fetchRemote();
|
|
123
126
|
|
|
124
|
-
const
|
|
125
|
-
const
|
|
127
|
+
const remoteBranchPath = path.join(artPath, 'history/remote', branch);
|
|
128
|
+
const localBranchPath = path.join(artPath, 'history/local', branch);
|
|
126
129
|
|
|
127
|
-
const
|
|
128
|
-
const localManifest = JSON.parse(fs.readFileSync(
|
|
130
|
+
const remoteManifest = JSON.parse(fs.readFileSync(path.join(remoteBranchPath, 'manifest.json'), 'utf8'));
|
|
131
|
+
const localManifest = JSON.parse(fs.readFileSync(path.join(localBranchPath, 'manifest.json'), 'utf8'));
|
|
129
132
|
|
|
130
133
|
const newCommits = remoteManifest.commits.filter(hash => !localManifest.commits.includes(hash));
|
|
131
134
|
|
|
@@ -134,100 +137,127 @@ async function pull () {
|
|
|
134
137
|
}
|
|
135
138
|
|
|
136
139
|
for (const commitHash of newCommits) {
|
|
137
|
-
const
|
|
138
|
-
const
|
|
140
|
+
const masterData = fs.readFileSync(path.join(remoteBranchPath, `${commitHash}.json`), 'utf8');
|
|
141
|
+
const masterJson = JSON.parse(masterData);
|
|
142
|
+
|
|
143
|
+
fs.writeFileSync(path.join(localBranchPath, `${commitHash}.json`), masterData);
|
|
139
144
|
|
|
140
|
-
|
|
141
|
-
path.join(
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
for (const partName of masterJson.parts) {
|
|
146
|
+
const partContent = fs.readFileSync(path.join(remoteBranchPath, partName), 'utf8');
|
|
147
|
+
|
|
148
|
+
fs.writeFileSync(path.join(localBranchPath, partName), partContent);
|
|
149
|
+
}
|
|
144
150
|
|
|
145
151
|
localManifest.commits.push(commitHash);
|
|
146
152
|
}
|
|
147
153
|
|
|
148
|
-
fs.writeFileSync(
|
|
154
|
+
fs.writeFileSync(path.join(localBranchPath, 'manifest.json'), JSON.stringify(localManifest, null, 2));
|
|
149
155
|
checkout(branch);
|
|
150
156
|
|
|
151
157
|
return `Applied ${newCommits.length} commits.`;
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
/**
|
|
155
|
-
* Uploads local
|
|
161
|
+
* Uploads local commits via the two-step /push and /commit/part flow.
|
|
156
162
|
*/
|
|
157
163
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
164
|
+
async function push () {
|
|
165
|
+
const artPath = path.join(process.cwd(), '.art');
|
|
166
|
+
const artJson = JSON.parse(fs.readFileSync(path.join(artPath, 'art.json'), 'utf8'));
|
|
167
|
+
|
|
168
|
+
if (!artJson.remote) throw new Error('Remote URL not configured.');
|
|
169
|
+
|
|
170
|
+
const branch = artJson.active.branch;
|
|
171
|
+
const token = artJson.configuration.personalAccessToken;
|
|
172
|
+
const remoteParts = artJson.remote.split('/');
|
|
173
|
+
const repo = remoteParts.pop();
|
|
174
|
+
const handle = remoteParts.pop();
|
|
175
|
+
|
|
176
|
+
const localBranchPath = path.join(artPath, 'history/local', branch);
|
|
177
|
+
const remoteBranchPath = path.join(artPath, 'history/remote', branch);
|
|
178
|
+
const localManifest = JSON.parse(fs.readFileSync(path.join(localBranchPath, 'manifest.json'), 'utf8'));
|
|
179
|
+
|
|
180
|
+
const response = await fetch(`${ARTIFACT_HOST}/manifest`, {
|
|
181
|
+
method: 'POST',
|
|
182
|
+
headers: { 'Content-Type': 'application/json' },
|
|
183
|
+
body: JSON.stringify({
|
|
184
|
+
type: 'history',
|
|
185
|
+
handle,
|
|
186
|
+
repo,
|
|
187
|
+
branch,
|
|
188
|
+
...(token && { personalAccessToken: token })
|
|
189
|
+
})
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const remoteManifest = await response.json();
|
|
193
|
+
const missingCommits = localManifest.commits.filter(hash => !remoteManifest.commits.includes(hash));
|
|
161
194
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
195
|
+
if (missingCommits.length === 0) {
|
|
196
|
+
return 'Everything up to date.';
|
|
197
|
+
}
|
|
165
198
|
|
|
166
|
-
|
|
167
|
-
const token = artJson.configuration.personalAccessToken;
|
|
168
|
-
const remoteParts = artJson.remote.split('/');
|
|
169
|
-
const repo = remoteParts.pop();
|
|
170
|
-
const handle = remoteParts.pop();
|
|
199
|
+
let rootData = null;
|
|
171
200
|
|
|
172
|
-
|
|
201
|
+
if (remoteManifest.commits.length === 0) {
|
|
202
|
+
const rootMasterPath = path.join(artPath, 'root/manifest.json');
|
|
173
203
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
body: JSON.stringify({
|
|
178
|
-
type: 'history',
|
|
179
|
-
handle,
|
|
180
|
-
repo,
|
|
181
|
-
branch,
|
|
204
|
+
if (fs.existsSync(rootMasterPath)) {
|
|
205
|
+
const master = JSON.parse(fs.readFileSync(rootMasterPath, 'utf8'));
|
|
206
|
+
const parts = {};
|
|
182
207
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
208
|
+
for (const partName of master.parts) {
|
|
209
|
+
parts[partName] = JSON.parse(fs.readFileSync(path.join(artPath, 'root', partName), 'utf8'));
|
|
210
|
+
}
|
|
186
211
|
|
|
187
|
-
|
|
188
|
-
|
|
212
|
+
rootData = { master, parts };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
189
215
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
216
|
+
const currentRemoteManifestPath = path.join(remoteBranchPath, 'manifest.json');
|
|
217
|
+
const currentRemoteManifest = fs.existsSync(currentRemoteManifestPath)
|
|
218
|
+
? JSON.parse(fs.readFileSync(currentRemoteManifestPath, 'utf8'))
|
|
219
|
+
: { commits: [] };
|
|
193
220
|
|
|
194
|
-
|
|
221
|
+
for (const commitHash of missingCommits) {
|
|
222
|
+
const commitMaster = JSON.parse(fs.readFileSync(path.join(localBranchPath, `${commitHash}.json`), 'utf8'));
|
|
195
223
|
|
|
196
|
-
|
|
197
|
-
|
|
224
|
+
const pushRes = await fetch(`${ARTIFACT_HOST}/push`, {
|
|
225
|
+
method: 'POST',
|
|
226
|
+
headers: { 'Content-Type': 'application/json' },
|
|
227
|
+
body: JSON.stringify({
|
|
228
|
+
handle, repo, branch, commit: commitMaster,
|
|
198
229
|
|
|
199
|
-
|
|
200
|
-
rootData = JSON.parse(fs.readFileSync(rootManifestPath, 'utf8'));
|
|
201
|
-
}
|
|
202
|
-
}
|
|
230
|
+
...(rootData && { root: rootData }),
|
|
203
231
|
|
|
204
|
-
|
|
205
|
-
|
|
232
|
+
...(token && { personalAccessToken: token })
|
|
233
|
+
})
|
|
234
|
+
});
|
|
206
235
|
|
|
207
|
-
|
|
208
|
-
method: 'POST',
|
|
209
|
-
headers: { 'Content-Type': 'application/json' },
|
|
210
|
-
body: JSON.stringify({
|
|
211
|
-
handle,
|
|
212
|
-
repo,
|
|
213
|
-
branch,
|
|
214
|
-
commit: commitData,
|
|
236
|
+
if (!pushRes.ok) throw new Error(`Server rejected push for commit ${commitHash}`);
|
|
215
237
|
|
|
216
|
-
|
|
238
|
+
rootData = null;
|
|
217
239
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
});
|
|
240
|
+
for (const partName of commitMaster.parts) {
|
|
241
|
+
const partData = JSON.parse(fs.readFileSync(path.join(localBranchPath, partName), 'utf8'));
|
|
221
242
|
|
|
222
|
-
|
|
223
|
-
|
|
243
|
+
const partRes = await fetch(`${ARTIFACT_HOST}/commit/part`, {
|
|
244
|
+
method: 'POST',
|
|
245
|
+
headers: { 'Content-Type': 'application/json' },
|
|
246
|
+
body: JSON.stringify({
|
|
247
|
+
handle, repo, branch, partName, partData,
|
|
248
|
+
...(token && { personalAccessToken: token })
|
|
249
|
+
})
|
|
250
|
+
});
|
|
224
251
|
|
|
225
|
-
|
|
252
|
+
if (!partRes.ok) throw new Error(`Server failed to receive part ${partName}`);
|
|
253
|
+
}
|
|
226
254
|
|
|
227
|
-
|
|
255
|
+
currentRemoteManifest.commits.push(commitHash);
|
|
256
|
+
fs.writeFileSync(currentRemoteManifestPath, JSON.stringify(currentRemoteManifest, null, 2));
|
|
257
|
+
}
|
|
228
258
|
|
|
229
|
-
|
|
230
|
-
|
|
259
|
+
return `Pushed ${missingCommits.length} commits to remote.`;
|
|
260
|
+
}
|
|
231
261
|
|
|
232
262
|
module.exports = {
|
|
233
263
|
__libraryVersion: pkg.version,
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* art - Modern version control.
|
|
3
|
-
* Core Library Entry Point (v0.
|
|
3
|
+
* Core Library Entry Point (v0.3.0)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const Setup = require('./setup');
|
|
@@ -50,7 +50,7 @@ const art = {
|
|
|
50
50
|
|
|
51
51
|
// Metadata
|
|
52
52
|
|
|
53
|
-
version: '0.
|
|
53
|
+
version: '0.3.0',
|
|
54
54
|
modules: [
|
|
55
55
|
Setup.__libraryAPIName,
|
|
56
56
|
Workflow.__libraryAPIName,
|