1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2025-02-21 06:29:02 +00:00

write README when creating github repository, check platform, migrate files

This commit is contained in:
Steven Hugg 2019-05-08 09:39:57 -04:00
parent 806687c31d
commit 4cc9aaeaca
9 changed files with 200 additions and 70 deletions

View File

@ -41,7 +41,6 @@ TODO:
- VCS asm library - VCS asm library
- better VCS single stepping, maybe also listings - better VCS single stepping, maybe also listings
- VCS skips step on lsr/lsr after run to line - VCS skips step on lsr/lsr after run to line
- links to external tools in ide
- error msg when #link doesn't work - error msg when #link doesn't work
- figure out folders for projects for real - figure out folders for projects for real
- click to break on raster position - click to break on raster position
@ -132,12 +131,19 @@ TODO:
- upload multiple files/zip file to subdirectory - upload multiple files/zip file to subdirectory
- allow "include graphics.asm" instead of "include project/graphics.asm" - allow "include graphics.asm" instead of "include project/graphics.asm"
- chrome looks blurry on vcs - chrome looks blurry on vcs
- convert more stuff to Promises
- don't have to include bootstrap-tourist each time? - don't have to include bootstrap-tourist each time?
- don't have to include firebase always? - don't have to include firebase always?
- Github - Github
- write/read metadata w/ main file
- push/pull changes
- gh-pages branch with embedded - gh-pages branch with embedded
- handle overwrite logic
- put extensions on vcs example files
- test edge/failure cases
- what to do about included files?
- what if files already open in editor
- import twice?
- un-bind from repo?
- login/logout?
WEB WORKER FORMAT WEB WORKER FORMAT

View File

@ -407,6 +407,7 @@ function require(modname) {
} }
</script> </script>
<script src="lib/octokat.js"></script>
<script src="tss/js/tss/PsgDeviceChannel.js"></script> <script src="tss/js/tss/PsgDeviceChannel.js"></script>
<script src="tss/js/tss/MasterChannel.js"></script> <script src="tss/js/tss/MasterChannel.js"></script>
<script src="tss/js/tss/AudioLooper.js"></script> <script src="tss/js/tss/AudioLooper.js"></script>

View File

@ -338,4 +338,19 @@ export class CodeProject {
} }
return path; return path;
} }
migrateToNewFolder(newprefix : string) {
// TODO: must end with /
var newPath = newprefix + this.stripLocalPath(this.mainPath);
console.log(this.mainPath + "->" + newPath);
var data = this.filedata[this.mainPath];
console.log(data.length + " bytes");
return this.store.setItem(newPath, data).then(() => {
//return this.store.removeItem(this.mainPath);
console.log("moved " + this.mainPath + " to " + newPath);
this.filedata[newPath] = this.filedata[this.mainPath]; //TODO?
this.mainPath = newPath;
});
}
} }

View File

@ -7,15 +7,18 @@ import { CodeProject } from "./project";
declare var exports; declare var exports;
declare var firebase; declare var firebase;
interface GHSession { export interface GHSession {
url : string; url : string;
user : string; user : string;
reponame : string; reponame : string;
repo : any; repo : any;
prefix : string; prefix : string;
paths? : string[]; paths? : string[];
mainPath?: string;
} }
const README_md_template = "$NAME\n=====\n\nCompatible with the [$PLATFORM](http://8bitworkshop.com/redir.html?platform=$PLATFORM&importURL=$GITHUBURL) platform in [8bitworkshop](http://8bitworkshop.com/). Main file is [$MAINFILE]($MAINFILE#mainfile).\n";
export class GithubService { export class GithubService {
github; github;
@ -39,7 +42,6 @@ export class GithubService {
parseGithubURL(ghurl:string) { parseGithubURL(ghurl:string) {
var toks = ghurl.split('/'); var toks = ghurl.split('/');
console.log(toks);
if (toks.length < 5) return null; if (toks.length < 5) return null;
if (toks[0] != 'https:') return null; if (toks[0] != 'https:') return null;
if (toks[2] != 'github.com') return null; if (toks[2] != 'github.com') return null;
@ -47,10 +49,10 @@ export class GithubService {
} }
getPrefix(user, reponame) : string { getPrefix(user, reponame) : string {
return 'shared/' + user + '-' + reponame + '/'; return 'shared/' + user + '/' + reponame + '/';
} }
getGithubRepo(ghurl:string) : Promise<GHSession> { getGithubSession(ghurl:string) : Promise<GHSession> {
return new Promise( (yes,no) => { return new Promise( (yes,no) => {
var urlparse = this.parseGithubURL(ghurl); var urlparse = this.parseGithubURL(ghurl);
if (!urlparse) { if (!urlparse) {
@ -70,9 +72,6 @@ export class GithubService {
// bind a folder path to the Github URL in local storage // bind a folder path to the Github URL in local storage
bind(sess : GHSession, dobind : boolean) { bind(sess : GHSession, dobind : boolean) {
var key = '__github_url_' + sess.prefix; var key = '__github_url_' + sess.prefix;
// TODO: this doesn't work b/c it binds the entire root to a url
if (!key.endsWith('/'))
key = key + '/';
console.log('bind', key, dobind); console.log('bind', key, dobind);
if (dobind) if (dobind)
localStorage.setItem(key, sess.url); localStorage.setItem(key, sess.url);
@ -83,13 +82,43 @@ export class GithubService {
getBoundURL(path : string) : string { getBoundURL(path : string) : string {
var p = getFolderForPath(path); var p = getFolderForPath(path);
var key = '__github_url_' + p + '/'; var key = '__github_url_' + p + '/';
console.log(key); console.log("getBoundURL", key);
return localStorage.getItem(key) as string; // TODO return localStorage.getItem(key) as string; // TODO
} }
import(ghurl:string) : Promise<GHSession> { import(ghurl:string) : Promise<GHSession> {
var sess : GHSession; var sess : GHSession;
return this.getGithubRepo(ghurl).then( (session) => { return this.getGithubSession(ghurl).then( (session) => {
sess = session;
// load README
return sess.repo.contents('README.md').read();
})
.catch( () => {
return ''; // empty README
})
.then( (readme) => {
var m;
// check README for main file
const re8main = /\(([^)]+)#mainfile\)/;
m = re8main.exec(readme);
if (m) {
console.log("main path: '" + m[1] + "'");
sess.mainPath = m[1];
}
// check README for proper platform
const re8plat = /8bitworkshop.com[^)]+platform=(\w+)/;
m = re8plat.exec(readme);
if (m && !this.project.platform_id.startsWith(m[1])) {
throw "Platform mismatch: Repository is " + m[1] + ", you have " + this.project.platform_id + " selected.";
}
// get head commit
return this.pull(ghurl);
});
}
pull(ghurl:string) : Promise<GHSession> {
var sess : GHSession;
return this.getGithubSession(ghurl).then( (session) => {
sess = session; sess = session;
return sess.repo.commits(this.branch).fetch(); return sess.repo.commits(this.branch).fetch();
}) })
@ -124,21 +153,32 @@ export class GithubService {
} }
publish(reponame:string, desc:string, license:string, isprivate:boolean) : Promise<GHSession> { publish(reponame:string, desc:string, license:string, isprivate:boolean) : Promise<GHSession> {
var repo;
return this.github.user.repos.create({ return this.github.user.repos.create({
name: reponame, name: reponame,
description: desc, description: desc,
private: isprivate, private: isprivate,
auto_init: true, auto_init: false,
license_template: license license_template: license
}) })
.then( (repo) => { .then( (_repo) => {
let sess = { repo = _repo;
url: repo.htmlUrl, // create README.md
user: repo.owner.login, var s = README_md_template;
reponame: reponame, s = s.replace(/\$NAME/g, reponame);
repo: repo, s = s.replace(/\$PLATFORM/g, this.project.platform_id);
prefix : '' s = s.replace(/\$IMPORTURL/g, repo.html_url);
}; s = s.replace(/\$MAINFILE/g, this.project.stripLocalPath(this.project.mainPath));
var config = {
message: '8bitworkshop: updated metadata in README.md',
content: btoa(s)
}
return repo.contents('README.md').add(config);
})
.then( () => {
return this.getGithubSession(repo.htmlUrl);
})
.then( (sess) => {
this.bind(sess, true); this.bind(sess, true);
return sess; return sess;
}); });
@ -149,7 +189,7 @@ export class GithubService {
var repo; var repo;
var head; var head;
var tree; var tree;
return this.getGithubRepo(ghurl).then( (session) => { return this.getGithubSession(ghurl).then( (session) => {
sess = session; sess = session;
repo = sess.repo; repo = sess.repo;
return repo.git.refs.heads(this.branch).fetch(); return repo.git.refs.heads(this.branch).fetch();

View File

@ -14,7 +14,7 @@ import { createNewPersistentStore } from "./store";
import { getFilenameForPath, getFilenamePrefix, highlightDifferences, invertMap, byteArrayToString, compressLZG, import { getFilenameForPath, getFilenamePrefix, highlightDifferences, invertMap, byteArrayToString, compressLZG,
byteArrayToUTF8, isProbablyBinary, getWithBinary } from "./util"; byteArrayToUTF8, isProbablyBinary, getWithBinary } from "./util";
import { StateRecorderImpl } from "./recorder"; import { StateRecorderImpl } from "./recorder";
import { GithubService } from "./services"; import { GHSession, GithubService } from "./services";
// external libs (TODO) // external libs (TODO)
declare var Tour, GIF, saveAs, JSZip, Mousetrap, Split, firebase; declare var Tour, GIF, saveAs, JSZip, Mousetrap, Split, firebase;
@ -382,35 +382,48 @@ function getCookie(name) {
function getGithubService() { function getGithubService() {
if (!githubService) { if (!githubService) {
loadScript("lib/octokat.js", () => {
// get github API key from cookie // get github API key from cookie
var ghkey = getCookie('__github_key'); var ghkey = getCookie('__github_key');
var ghopts = {token:ghkey}; var ghopts = {token:ghkey};
githubService = new GithubService(new exports['Octokat'](ghopts), store, current_project); githubService = new GithubService(new exports['Octokat'](ghopts), store, current_project);
}); console.log("loaded github service");
} }
return githubService; return githubService;
} }
function getBoundGithubURL() {
console.log("main path: " + current_project.mainPath);
var ghurl = getGithubService().getBoundURL(current_project.mainPath);
console.log("Github URL: " + ghurl);
return ghurl || alert("This project (" + current_project.mainPath + ") is not bound to a GitHub project.");
}
function _importProjectFromGithub(e) { function _importProjectFromGithub(e) {
getGithubService(); // load it
var modal = $("#importGithubModal"); var modal = $("#importGithubModal");
var btn = $("#importGithubButton"); var btn = $("#importGithubButton");
modal.modal('show'); modal.modal('show');
btn.off('click').on('click', () => { btn.off('click').on('click', () => {
var githuburl = $("#importGithubURL").val()+""; var githuburl = $("#importGithubURL").val()+"";
getGithubService().import(githuburl).then( (sess) => { getGithubService().import(githuburl).then( (sess:GHSession) => {
if (sess.mainPath) {
reloadPresetNamed(sess.prefix + sess.mainPath);
}
// TODO : redirect to main file // TODO : redirect to main file
modal.modal('hide'); modal.modal('hide');
}).catch( (e) => { }).catch( (e) => {
modal.modal('hide'); modal.modal('hide');
console.log(e);
alert("Could not import " + githuburl + ": " + e); alert("Could not import " + githuburl + ": " + e);
}); });
}); });
} }
function _publishProjectToGithub(e) { function _publishProjectToGithub(e) {
getGithubService(); // load it var ghurl = getGithubService().getBoundURL(current_project.mainPath);
if (ghurl) {
alert("This project (" + current_project.mainPath + ") is already bound to a Github repository. Choose 'Push Changes' to update.");
return;
}
var modal = $("#publishGithubModal"); var modal = $("#publishGithubModal");
var btn = $("#publishGithubButton"); var btn = $("#publishGithubButton");
modal.modal('show'); modal.modal('show');
@ -419,22 +432,28 @@ function _publishProjectToGithub(e) {
var desc = $("#githubRepoDesc").val()+""; var desc = $("#githubRepoDesc").val()+"";
var priv = $("#githubRepoPrivate").val() == 'private'; var priv = $("#githubRepoPrivate").val() == 'private';
var license = $("#githubRepoLicense").val()+""; var license = $("#githubRepoLicense").val()+"";
getGithubService().publish(name, desc, license, priv).then( (sess) => { var sess;
modal.modal('hide'); modal.modal('hide');
// TODO: commit files setWaitDialog(true);
// TODO: migrate project files getGithubService().publish(name, desc, license, priv).then( (_sess) => {
sess = _sess;
console.log(sess); console.log(sess);
alert("Created repository at " + sess.url); return current_project.migrateToNewFolder(sess.prefix);
pushChangesToGithub('initial import from 8bitworkshop.com'); }).then( () => {
return pushChangesToGithub('initial import from 8bitworkshop.com');
}).then( () => {
reloadPresetNamed(current_project.mainPath);
}).catch( (e) => { }).catch( (e) => {
modal.modal('hide'); setWaitDialog(false);
alert("Could not create GitHub repository: " + e); console.log(e);
alert("Could not publish GitHub repository: " + e);
}); });
}); });
} }
function _pushProjectToGithub(e) { function _pushProjectToGithub(e) {
getGithubService(); // load it var ghurl = getBoundGithubURL();
if (!ghurl) return;
var modal = $("#pushGithubModal"); var modal = $("#pushGithubModal");
var btn = $("#pushGithubButton"); var btn = $("#pushGithubButton");
modal.modal('show'); modal.modal('show');
@ -445,29 +464,42 @@ function _pushProjectToGithub(e) {
}); });
} }
function pushChangesToGithub(message:string) { function _pullProjectFromGithub(e) {
var ghurl = getGithubService().getBoundURL(current_project.mainPath); var ghurl = getBoundGithubURL();
console.log("Github URL: " + ghurl); if (!ghurl) return;
if (!ghurl) { setWaitDialog(true);
alert("This project is not bound to a GitHub project."); getGithubService().pull(ghurl).then( (sess:GHSession) => {
return; setWaitDialog(false);
});
} }
function pushChangesToGithub(message:string) {
var ghurl = getBoundGithubURL();
if (!ghurl) return;
// build file list for push // build file list for push
var files = []; var files = [];
for (var path in current_project.filedata) { for (var path in current_project.filedata) {
var newpath = current_project.stripLocalPath(path); var newpath = current_project.stripLocalPath(path);
files.push({path:newpath, data:current_project.filedata[path]}); var data = current_project.filedata[path];
if (newpath && data) {
files.push({path:newpath, data:data});
}
} }
// push files // push files
setWaitDialog(true); setWaitDialog(true);
getGithubService().commitPush(ghurl, message, files).then( (sess) => { return getGithubService().commitPush(ghurl, message, files).then( (sess) => {
setWaitDialog(false); setWaitDialog(false);
alert("Pushed files to " + ghurl);
return sess;
}).catch( (e) => {
setWaitDialog(false);
console.log(e);
alert("Could not push GitHub repository: " + e);
}); });
} }
// TODO: remove? // TODO: remove?
function loadSharedGist(gistkey : string) { function loadSharedGist(gistkey : string) {
loadScript("lib/octokat.js", () => {
var github = new exports['Octokat'](); var github = new exports['Octokat']();
var gist = this.github.gists(gistkey); var gist = this.github.gists(gistkey);
gist.fetch().done( (val) => { gist.fetch().done( (val) => {
@ -486,7 +518,6 @@ function loadSharedGist(gistkey : string) {
}).fail(function(err) { }).fail(function(err) {
alert("Error loading share file: " + err.message); alert("Error loading share file: " + err.message);
}); });
});
} }
function _shareEmbedLink(e) { function _shareEmbedLink(e) {
@ -1244,6 +1275,7 @@ function setupDebugControls() {
$("#item_github_import").click(_importProjectFromGithub); $("#item_github_import").click(_importProjectFromGithub);
$("#item_github_publish").click(_publishProjectToGithub); $("#item_github_publish").click(_publishProjectToGithub);
$("#item_github_push").click(_pushProjectToGithub); $("#item_github_push").click(_pushProjectToGithub);
$("#item_github_pull").click(_pullProjectFromGithub);
$("#item_share_file").click(_shareEmbedLink); $("#item_share_file").click(_shareEmbedLink);
$("#item_reset_file").click(_revertFile); $("#item_reset_file").click(_revertFile);
$("#item_rename_file").click(_renameFile); $("#item_rename_file").click(_renameFile);

View File

@ -115,18 +115,15 @@ export class ProjectWindows {
} }
updateFile(fileid:string, data:FileData) { updateFile(fileid:string, data:FileData) {
// is there an editor? we should create one... // is there an editor? if so, use it
var wnd = this.create(fileid); var wnd = this.id2window[fileid];
if (wnd && wnd.setText && typeof data === 'string') { if (wnd && wnd.setText && typeof data === 'string') {
wnd.setText(data); wnd.setText(data);
this.undofiles.push(fileid);
} else { } else {
this.project.updateFile(fileid, data); this.project.updateFile(fileid, data);
if (wnd) {
wnd.refresh(false);
} }
} }
this.undofiles.push(fileid);
}
undoStep() { undoStep() {
var fileid = this.undofiles.pop(); var fileid = this.undofiles.pop();

View File

@ -14,29 +14,67 @@ var Octokat = require('octokat');
var test_platform_id = "_TEST"; var test_platform_id = "_TEST";
function newGH(store) { function newGH(store, platform_id) {
// pzpinfo user // pzpinfo user
return new serv.GithubService(new Octokat({token:'ec64fdd81dedab8b7547388eabef09288e9243a9'}), store); var project = new prj.CodeProject({}, platform_id||test_platform_id, null, store);
project.mainPath = 'local/main.asm';
project.updateFileInStore(project.mainPath, '\torg $0 ; test\n');
return new serv.GithubService(new Octokat({token:'ec64fdd81dedab8b7547388eabef09288e9243a9'}), store, project);
} }
const t0 = new Date().getTime();
describe('Store', function() { describe('Store', function() {
it('Should import from Github', function(done) { it('Should import from Github (check README)', function(done) {
var store = mstore.createNewPersistentStore(test_platform_id, function(store) { var store = mstore.createNewPersistentStore(test_platform_id, function(store) {
var gh = newGH(store); var gh = newGH(store);
gh.import('https://github.com/sehugg/genemedic/extra/garbage').then( (sess) => { gh.import('https://github.com/pzpinfo/testrepo1557322631070').then( (sess) => {
assert.equal(4, sess.paths.length); console.log(sess.paths);
assert.equal(2, sess.paths.length);
// TODO: test for presence in local storage, make sure returns keys // TODO: test for presence in local storage, make sure returns keys
done(); done();
}); });
}); });
}); });
it('Should import from Github (no README)', function(done) {
var store = mstore.createNewPersistentStore(test_platform_id, function(store) {
var gh = newGH(store);
gh.import('https://github.com/pzpinfo/testrepo3').then( (sess) => {
console.log(sess.paths);
assert.equal(3, sess.paths.length);
// TODO: test for presence in local storage, make sure returns keys
done();
});
});
});
it('Should import from Github (wrong platform)', function(done) {
var store = mstore.createNewPersistentStore('_FOO', function(store) {
var gh = newGH(store, '_FOO');
gh.import('https://github.com/pzpinfo/testrepo1557326056720').catch( (e) => {
assert.ok(e.startsWith('Platform mismatch'));
done();
});
});
});
it('Should publish (fail) on Github', function(done) { it('Should publish (fail) on Github', function(done) {
var store = mstore.createNewPersistentStore(test_platform_id, function(store) { var store = mstore.createNewPersistentStore(test_platform_id, function(store) {
var gh = newGH(store); var gh = newGH(store);
// should fail // should fail
gh.publish('testrepo4').catch( (e) => { gh.publish('testrepo1').catch( (e) => {
done();
});
});
});
it('Should publish new repository on Github', function(done) {
var store = mstore.createNewPersistentStore(test_platform_id, function(store) {
var gh = newGH(store);
// should fail
gh.publish('testrepo'+t0, "new description", "mit", false).then( (sess) => {
done(); done();
}); });
}); });
@ -57,11 +95,11 @@ describe('Store', function() {
it('Should bind paths to Github', function(done) { it('Should bind paths to Github', function(done) {
var store = mstore.createNewPersistentStore(test_platform_id, function(store) { var store = mstore.createNewPersistentStore(test_platform_id, function(store) {
var gh = newGH(store); var gh = newGH(store);
var sess = {prefix:'prefix', url:'_'}; var sess = {prefix:'shared/foo/bar/', url:'_'};
gh.bind(sess, true); gh.bind(sess, true);
assert.equal(gh.getBoundURL('prefix', '_')); assert.equal(gh.getBoundURL('shared/foo/bar/'), '_');
gh.bind(sess, false); gh.bind(sess, false);
assert.equal(gh.getBoundURL('prefix', null)); assert.equal(gh.getBoundURL('shared/foo/bar/'), null);
done(); done();
}); });
}); });

View File

@ -13,8 +13,6 @@ global.window = dom.window;
global.document = dom.window.document; global.document = dom.window.document;
dom.window.Audio = null; dom.window.Audio = null;
global.Image = function() { } global.Image = function() { }
global.btoa = require('btoa');
global.atob = require('atob');
global['$'] = require("jquery/jquery-2.2.3.min.js"); global['$'] = require("jquery/jquery-2.2.3.min.js");
global.includeInThisContext('src/cpu/z80fast.js'); global.includeInThisContext('src/cpu/z80fast.js');
includeInThisContext("javatari.js/release/javatari/javatari.js"); includeInThisContext("javatari.js/release/javatari/javatari.js");

View File

@ -8,6 +8,9 @@ var worker = {};
global.window = global; global.window = global;
global.exports = {}; global.exports = {};
global.btoa = require('btoa');
global.atob = require('atob');
global.includeInThisContext = function(path) { global.includeInThisContext = function(path) {
var code = fs.readFileSync(path); var code = fs.readFileSync(path);
vm.runInThisContext(code, path); vm.runInThisContext(code, path);