factorio-test-cli 2.0.0 → 2.0.1
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/package.json +1 -1
- package/run.js +118 -6
package/package.json
CHANGED
package/run.js
CHANGED
|
@@ -7,6 +7,9 @@ import { spawn, spawnSync } from "child_process";
|
|
|
7
7
|
import BufferLineSplitter from "./buffer-line-splitter.js";
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
import * as process from "node:process";
|
|
10
|
+
import * as https from "https";
|
|
11
|
+
import * as readline from "readline";
|
|
12
|
+
const FACTORIO_TEST_MOD_VERSION = "2.0.1";
|
|
10
13
|
const thisCommand = program
|
|
11
14
|
.command("run")
|
|
12
15
|
.summary("Runs tests with Factorio test.")
|
|
@@ -117,12 +120,121 @@ async function checkModExists(modsDir, modName) {
|
|
|
117
120
|
}
|
|
118
121
|
async function installFactorioTest(modsDir) {
|
|
119
122
|
await fsp.mkdir(modsDir, { recursive: true });
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
const modName = "factorio-test";
|
|
124
|
+
const version = FACTORIO_TEST_MOD_VERSION;
|
|
125
|
+
const expectedZipName = `${modName}_${version}.zip`;
|
|
126
|
+
const expectedZipPath = path.join(modsDir, expectedZipName);
|
|
127
|
+
if (fs.existsSync(expectedZipPath)) {
|
|
128
|
+
if (thisCommand.opts().verbose)
|
|
129
|
+
console.log(`${modName} version ${version} already installed`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
console.log(`Downloading ${modName} version ${version} from mod portal...`);
|
|
133
|
+
await downloadModVersion(modName, version, expectedZipPath);
|
|
134
|
+
}
|
|
135
|
+
async function downloadModVersion(modName, version, destPath) {
|
|
136
|
+
const modInfo = await fetchJson(`https://mods.factorio.com/api/mods/${modName}`);
|
|
137
|
+
const release = modInfo.releases.find((r) => r.version === version);
|
|
138
|
+
if (!release) {
|
|
139
|
+
const availableVersions = modInfo.releases.map((r) => r.version).join(", ");
|
|
140
|
+
throw new Error(`Version ${version} not found for mod ${modName}. Available: ${availableVersions}`);
|
|
141
|
+
}
|
|
142
|
+
const credentials = await getFactorioCredentials();
|
|
143
|
+
const downloadUrl = `https://mods.factorio.com${release.download_url}?username=${encodeURIComponent(credentials.username)}&token=${encodeURIComponent(credentials.token)}`;
|
|
144
|
+
await downloadFile(downloadUrl, destPath);
|
|
145
|
+
}
|
|
146
|
+
async function fetchJson(url) {
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
https.get(url, (res) => {
|
|
149
|
+
if (res.statusCode !== 200) {
|
|
150
|
+
reject(new Error(`HTTP ${res.statusCode} fetching ${url}`));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
let data = "";
|
|
154
|
+
res.on("data", (chunk) => (data += chunk));
|
|
155
|
+
res.on("end", () => {
|
|
156
|
+
try {
|
|
157
|
+
resolve(JSON.parse(data));
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
reject(new Error(`Failed to parse JSON from ${url}`, { cause: e }));
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
res.on("error", reject);
|
|
164
|
+
}).on("error", reject);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
async function downloadFile(url, destPath) {
|
|
168
|
+
return new Promise((resolve, reject) => {
|
|
169
|
+
https.get(url, (res) => {
|
|
170
|
+
if (res.statusCode === 302 || res.statusCode === 301) {
|
|
171
|
+
const redirectUrl = res.headers.location;
|
|
172
|
+
if (!redirectUrl) {
|
|
173
|
+
reject(new Error("Redirect without location header"));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
downloadFile(redirectUrl, destPath).then(resolve, reject);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (res.statusCode !== 200) {
|
|
180
|
+
reject(new Error(`HTTP ${res.statusCode} downloading mod`));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const fileStream = fs.createWriteStream(destPath);
|
|
184
|
+
res.pipe(fileStream);
|
|
185
|
+
fileStream.on("close", () => resolve());
|
|
186
|
+
fileStream.on("error", (err) => {
|
|
187
|
+
fs.unlink(destPath, () => { });
|
|
188
|
+
reject(err);
|
|
189
|
+
});
|
|
190
|
+
}).on("error", reject);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async function getFactorioCredentials() {
|
|
194
|
+
const playerDataPath = getPlayerDataPath();
|
|
195
|
+
if (playerDataPath && fs.existsSync(playerDataPath)) {
|
|
196
|
+
try {
|
|
197
|
+
const playerData = JSON.parse(await fsp.readFile(playerDataPath, "utf8"));
|
|
198
|
+
if (playerData["service-username"] && playerData["service-token"]) {
|
|
199
|
+
return {
|
|
200
|
+
username: playerData["service-username"],
|
|
201
|
+
token: playerData["service-token"],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// Fall through to prompt
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
console.log("Factorio credentials required for mod portal download.");
|
|
210
|
+
return promptForCredentials();
|
|
211
|
+
}
|
|
212
|
+
function getPlayerDataPath() {
|
|
213
|
+
const platform = os.platform();
|
|
214
|
+
if (platform === "linux") {
|
|
215
|
+
return path.join(os.homedir(), ".factorio", "player-data.json");
|
|
216
|
+
}
|
|
217
|
+
else if (platform === "darwin") {
|
|
218
|
+
return path.join(os.homedir(), "Library", "Application Support", "factorio", "player-data.json");
|
|
219
|
+
}
|
|
220
|
+
else if (platform === "win32") {
|
|
221
|
+
return path.join(os.homedir(), "AppData", "Roaming", "Factorio", "player-data.json");
|
|
222
|
+
}
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
async function promptForCredentials() {
|
|
226
|
+
const rl = readline.createInterface({
|
|
227
|
+
input: process.stdin,
|
|
228
|
+
output: process.stdout,
|
|
229
|
+
});
|
|
230
|
+
const question = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
|
|
231
|
+
try {
|
|
232
|
+
const username = await question("Factorio username: ");
|
|
233
|
+
const token = await question("Factorio token (from https://factorio.com/profile): ");
|
|
234
|
+
return { username, token };
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
rl.close();
|
|
126
238
|
}
|
|
127
239
|
}
|
|
128
240
|
async function ensureConfigIni(dataDir) {
|