-
-
Save laverdet/b67db14ccc8520abea2c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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(); |
Line 19 is spitting a lot of errors because obj is null
It should become
if (obj && typeof obj.main === 'string' && modules) {
Edit: This issue is fixed
Updated gist with fix for null clones (fixes survival) and also fixed console.log() redirection with longer messages
Thanks!
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
Thanks, this is great! Nice idea!
It didin't work for me (ubuntu 14.04), because of line 90:
at that point the file doesn’t exist (due to something in my vim configuration I presume), so I added a timeout as a quick-and-dirty fix.