Skip to content

Instantly share code, notes, and snippets.

@laverdet

laverdet/README Secret

Last active Jun 5, 2021
Embed
What would you like to do?
Usage:
./sync --local
This will update the code directly through a browser hook. This works much faster
while editing code in simulation mode, and can even be used while offline.
cat sync.crx.b64 | base64 -D > sync.crx
This is an extension you can add to Chrome to avoid having to paste the bootstrap
line in each time.
./sync --auth 'username@example.com:password'
This will sync code via the Screeps Grunt API
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
#!/usr/bin/env node
"use strict";
let https = require('https');
let path = require('path');
let fs = require('fs');
let URL = require('url');
let child_process = require('child_process');
// Read args
let argv = {};
for (let ii = 2; ii < process.argv.length; ++ii) {
if (process.argv[ii].substr(0, 2) === '--') {
if (process.argv[ii + 1] === undefined || process.argv[ii + 1].substr(0, 2) === '--') {
argv[process.argv[ii].substr(2)] = true;
} else {
argv[process.argv[ii].substr(2)] = process.argv[ii + 1];
}
}
}
// Usage
if (argv.local !== true && !argv.auth) {
console.log('usage: '+ path.basename(process.argv[0])+ ' '+ path.basename(process.argv[1])+ ' --local | --auth "email:password"');
process.exit();
}
// Watch git
function parseBranch(HEAD) {
return HEAD === 'ref: refs/heads/master\n' ? 'master' : 'dev';
}
let branch = parseBranch(fs.readFileSync('.git/HEAD', 'utf8'));
!argv.local && fs.watch('.git', function(ev, file) {
if (file === 'HEAD') {
let tmp = parseBranch(fs.readFileSync('.git/HEAD', 'utf8'));
if (tmp !== branch) {
branch = tmp;
console.log('Switching to: '+ branch);
}
}
});
// Read local code from disk
let modules = {};
function refreshLocalBranch() {
modules = {};
fs.readdirSync('.').forEach(function(file) {
if (file !== 'sync.js' && /\.js$/.test(file)) {
modules[file.replace( /\.js$/, '')] = fs.readFileSync(file, 'utf8');
}
});
modules['last-push'] = 'module.exports='+ Date.now()+ ';';
}
// Watch for local changes
let pushTimeout;
fs.watch('.', function(ev, file) {
if (file !== 'sync.js' && /\.js$/.test(file)) {
try {
modules[file.replace(/\.js$/, '')] = fs.readFileSync(file, 'utf8');
} catch (err) {
delete modules[file.replace(/\.js$/, '')];
}
modules['last-push'] = 'module.exports='+ Date.now()+ ';';
schedulePush();
}
});
// Push changes to screeps.com
let writeListener;
function schedulePush() {
if (pushTimeout) {
clearTimeout(pushTimeout);
}
pushTimeout = setTimeout(function() {
pushTimeout = undefined;
writeListener && writeListener();
}, 50);
}
if (argv.local) {
// Auto-generation of self-signed certificate
let sslKey = new Promise(function(resolve, reject) {
function generate() {
child_process.execFile('openssl', [ 'genrsa', '-des3', '-out', 'sync.key', '-passout', 'pass:password', 2048 ], function(err, stdout, stderr) {
if (err) {
fs.unlink('sync.key', () => 0);
return reject(err);
}
child_process.execFile('openssl', [ 'req', '-new', '-batch', '-subj', '/commonName=127.0.0.1', '-key', 'sync.key', '-out', 'sync.csr', '-passin', 'pass:password' ], function(err, stdout, stderr) {
if (err) {
fs.unlink('sync.key', () => 0);
fs.unlink('sync.csr', () => 0);
return reject(err);
}
child_process.execFile('openssl', [ 'x509', '-req', '-days', 3650, '-in', 'sync.csr', '-signkey', 'sync.key', '-out', 'sync.crt', '-passin', 'pass:password' ], function(err, stdout, stderr) {
fs.unlink('sync.csr', () => 0);
if (err) {
fs.unlink('sync.key', () => 0);
fs.unlink('sync.crt', () => 0);
return reject(err);
}
fs.readFile('sync.key', function(err, key) {
if (err) return reject(err);
fs.readFile('sync.crt', function(err, cert) {
if (err) return reject(err);
resolve({ key, cert });
});
});
});
});
});
}
fs.readFile('sync.key', function(err, key) {
if (err) return generate();
fs.readFile('sync.crt', function(err, cert) {
if (err) return generate();
resolve({ key, cert });
});
});
});
// This all runs in the browser
let clientSide = function() {
function wait() {
if (document.evaluate("//div[contains(@class, 'console-messages-list')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue) {
// Grab reference to the commit button
let buttons = Array.prototype.slice.call(document.body.getElementsByTagName('button')).filter(function(el) {
return el.getAttribute('ng:disabled') === '!Script.dirty';
});
let commitButton = buttons[0];
// Override lodash's cloneDeep which is called from inside the internal reset method
let modules;
_.cloneDeep = function(cloneDeep) {
return function(obj) {
if (obj && typeof obj.main === 'string' && modules) {
// Monkey patch!
return modules;
}
return cloneDeep.apply(this, arguments);
};
}(_.cloneDeep);
// Wait for changes to local filesystem
function update(now) {
let req = new XMLHttpRequest;
req.onreadystatechange = function() {
if (req.readyState === 4) {
if (req.status === 200) {
modules = JSON.parse(req.responseText);
commitButton.disabled = false;
commitButton.click();
}
setTimeout(update.bind(this, false), req.status === 200 ? 0 : 1000);
}
};
req.open('GET', '//127.0.0.1:9090/'+ (now ? 'get' : 'wait'), true);
req.send();
};
update(true);
// Look for console messages
let sconsole = document.body.getElementsByClassName('console-messages-list')[0];
let lastMessage;
setInterval(function() {
let nodes = sconsole.getElementsByClassName('console-message');
let messages = [];
let found = false;
for (let ii = nodes.length - 1; ii >= 0; --ii) {
let el = nodes[ii];
let ts = el.getElementsByClassName('timestamp')[0];
ts = ts && ts.firstChild.nodeValue;
let msg = el.getElementsByTagName('span')[0].childNodes;
let txt = '';
for (let jj = 0; jj < msg.length; ++jj) {
if (msg[jj].tagName === 'BR') {
txt += '\n';
} else if (msg[jj].tagName === 'ANONYMOUS') {
msg = msg[jj].childNodes;
jj = -1;
} else {
txt += msg[jj].nodeValue;
}
}
if (lastMessage && txt === lastMessage[1] && ts === lastMessage[0]) {
break;
}
messages.push([ts, txt]);
}
if (messages.length) {
let req = new XMLHttpRequest;
req.open('GET', '//127.0.0.1:9090/log?log='+ encodeURIComponent(JSON.stringify(messages.reverse())), true);
req.send();
lastMessage = messages[messages.length - 1];
}
}, 100);
} else {
setTimeout(wait, 100);
}
}
wait();
};
// Localhost HTTP server
sslKey.then(function(key) {
let server = https.createServer({
key: key.key,
cert: key.cert,
passphrase: 'password',
}, function(req, res) {
let path = URL.parse(req.url, true);
switch (path.pathname) {
case '/inject':
res.writeHead(200, { 'Content-Type': 'text/javascript' });
res.end('~'+ clientSide.toString()+ '()');
break;
case '/get':
case '/wait':
if (writeListener) {
writeListener();
}
writeListener = function() {
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
res.end(JSON.stringify(modules));
writeListener = undefined;
};
if (req.url === '/get') {
writeListener();
}
break;
case '/log':
res.writeHead(200, { 'Access-Control-Allow-Origin': '*' });
res.end();
let messages = JSON.parse(path.query.log);
for (let ii = 0; ii < messages.length; ++ii) {
if (messages[ii][0]) {
let prefix = ' ';
for (let jj = messages[ii][0].length; jj > 0; --jj) {
prefix += ' ';
}
console.log(
messages[ii][0],
messages[ii][1].split(/\n/g).map(function(line, ii) {
return (ii ? prefix : '')+ line;
}).join('\n')
);
} else {
console.log(messages[ii][1]);
}
}
break;
default:
res.writeHead(400);
res.end();
break;
}
});
server.timeout = 0;
server.listen(9090);
console.log(
"If you haven't done this already, run this (for OS X):\n"+
"sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain sync.crt\n\n"+
"Paste this into JS debug console in Screeps (*not* the Screeps console):\n"+
"var s = document.createElement('script');s.src='https://127.0.0.1:9090/inject';document.body.appendChild(s);"
);
}).catch(function(err) {
process.nextTick(() => { throw err });
});
} else {
// Push new code via Screeps API
writeListener = function() {
let req = https.request({
hostname: 'screeps.com',
port: 443,
path: '/api/user/code',
method: 'POST',
auth: argv.auth,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
});
req.end(JSON.stringify({ branch: branch, modules: modules }));
req.on('response', function(res) {
console.log('HTTP Status '+ res.statusCode);
});
};
}
// Sync current code
refreshLocalBranch();
schedulePush();
@SystemParadox
Copy link

SystemParadox commented Jan 8, 2015

For anyone else who is confused, this script does not cause the scripts to change in the screeps UI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment