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
- better VCS single stepping, maybe also listings
- VCS skips step on lsr/lsr after run to line
- links to external tools in ide
- error msg when #link doesn't work
- figure out folders for projects for real
- click to break on raster position
@ -132,12 +131,19 @@ TODO:
- upload multiple files/zip file to subdirectory
- allow "include graphics.asm" instead of "include project/graphics.asm"
- chrome looks blurry on vcs
- convert more stuff to Promises
- don't have to include bootstrap-tourist each time?
- don't have to include firebase always?
- 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

View File

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

View File

@ -338,4 +338,19 @@ export class CodeProject {
}
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 firebase;
interface GHSession {
export interface GHSession {
url : string;
user : string;
reponame : string;
repo : any;
prefix : 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 {
github;
@ -39,7 +42,6 @@ export class GithubService {
parseGithubURL(ghurl:string) {
var toks = ghurl.split('/');
console.log(toks);
if (toks.length < 5) return null;
if (toks[0] != 'https:') return null;
if (toks[2] != 'github.com') return null;
@ -47,10 +49,10 @@ export class GithubService {
}
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) => {
var urlparse = this.parseGithubURL(ghurl);
if (!urlparse) {
@ -70,9 +72,6 @@ export class GithubService {
// bind a folder path to the Github URL in local storage
bind(sess : GHSession, dobind : boolean) {
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);
if (dobind)
localStorage.setItem(key, sess.url);
@ -83,13 +82,43 @@ export class GithubService {
getBoundURL(path : string) : string {
var p = getFolderForPath(path);
var key = '__github_url_' + p + '/';
console.log(key);
console.log("getBoundURL", key);
return localStorage.getItem(key) as string; // TODO
}
import(ghurl:string) : Promise<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;
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> {
var repo;
return this.github.user.repos.create({
name: reponame,
description: desc,
private: isprivate,
auto_init: true,
auto_init: false,
license_template: license
})
.then( (repo) => {
let sess = {
url: repo.htmlUrl,
user: repo.owner.login,
reponame: reponame,
repo: repo,
prefix : ''
};
.then( (_repo) => {
repo = _repo;
// create README.md
var s = README_md_template;
s = s.replace(/\$NAME/g, reponame);
s = s.replace(/\$PLATFORM/g, this.project.platform_id);
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);
return sess;
});
@ -149,7 +189,7 @@ export class GithubService {
var repo;
var head;
var tree;
return this.getGithubRepo(ghurl).then( (session) => {
return this.getGithubSession(ghurl).then( (session) => {
sess = session;
repo = sess.repo;
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,
byteArrayToUTF8, isProbablyBinary, getWithBinary } from "./util";
import { StateRecorderImpl } from "./recorder";
import { GithubService } from "./services";
import { GHSession, GithubService } from "./services";
// external libs (TODO)
declare var Tour, GIF, saveAs, JSZip, Mousetrap, Split, firebase;
@ -382,35 +382,48 @@ function getCookie(name) {
function getGithubService() {
if (!githubService) {
loadScript("lib/octokat.js", () => {
// get github API key from cookie
var ghkey = getCookie('__github_key');
var ghopts = {token:ghkey};
githubService = new GithubService(new exports['Octokat'](ghopts), store, current_project);
});
// get github API key from cookie
var ghkey = getCookie('__github_key');
var ghopts = {token:ghkey};
githubService = new GithubService(new exports['Octokat'](ghopts), store, current_project);
console.log("loaded github service");
}
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) {
getGithubService(); // load it
var modal = $("#importGithubModal");
var btn = $("#importGithubButton");
modal.modal('show');
btn.off('click').on('click', () => {
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
modal.modal('hide');
}).catch( (e) => {
modal.modal('hide');
console.log(e);
alert("Could not import " + githuburl + ": " + 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 btn = $("#publishGithubButton");
modal.modal('show');
@ -419,22 +432,28 @@ function _publishProjectToGithub(e) {
var desc = $("#githubRepoDesc").val()+"";
var priv = $("#githubRepoPrivate").val() == 'private';
var license = $("#githubRepoLicense").val()+"";
getGithubService().publish(name, desc, license, priv).then( (sess) => {
modal.modal('hide');
// TODO: commit files
// TODO: migrate project files
var sess;
modal.modal('hide');
setWaitDialog(true);
getGithubService().publish(name, desc, license, priv).then( (_sess) => {
sess = _sess;
console.log(sess);
alert("Created repository at " + sess.url);
pushChangesToGithub('initial import from 8bitworkshop.com');
return current_project.migrateToNewFolder(sess.prefix);
}).then( () => {
return pushChangesToGithub('initial import from 8bitworkshop.com');
}).then( () => {
reloadPresetNamed(current_project.mainPath);
}).catch( (e) => {
modal.modal('hide');
alert("Could not create GitHub repository: " + e);
setWaitDialog(false);
console.log(e);
alert("Could not publish GitHub repository: " + e);
});
});
}
function _pushProjectToGithub(e) {
getGithubService(); // load it
var ghurl = getBoundGithubURL();
if (!ghurl) return;
var modal = $("#pushGithubModal");
var btn = $("#pushGithubButton");
modal.modal('show');
@ -445,29 +464,42 @@ function _pushProjectToGithub(e) {
});
}
function _pullProjectFromGithub(e) {
var ghurl = getBoundGithubURL();
if (!ghurl) return;
setWaitDialog(true);
getGithubService().pull(ghurl).then( (sess:GHSession) => {
setWaitDialog(false);
});
}
function pushChangesToGithub(message:string) {
var ghurl = getGithubService().getBoundURL(current_project.mainPath);
console.log("Github URL: " + ghurl);
if (!ghurl) {
alert("This project is not bound to a GitHub project.");
return;
}
var ghurl = getBoundGithubURL();
if (!ghurl) return;
// build file list for push
var files = [];
for (var path in current_project.filedata) {
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
setWaitDialog(true);
getGithubService().commitPush(ghurl, message, files).then( (sess) => {
return getGithubService().commitPush(ghurl, message, files).then( (sess) => {
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?
function loadSharedGist(gistkey : string) {
loadScript("lib/octokat.js", () => {
var github = new exports['Octokat']();
var gist = this.github.gists(gistkey);
gist.fetch().done( (val) => {
@ -486,7 +518,6 @@ function loadSharedGist(gistkey : string) {
}).fail(function(err) {
alert("Error loading share file: " + err.message);
});
});
}
function _shareEmbedLink(e) {
@ -1244,6 +1275,7 @@ function setupDebugControls() {
$("#item_github_import").click(_importProjectFromGithub);
$("#item_github_publish").click(_publishProjectToGithub);
$("#item_github_push").click(_pushProjectToGithub);
$("#item_github_pull").click(_pullProjectFromGithub);
$("#item_share_file").click(_shareEmbedLink);
$("#item_reset_file").click(_revertFile);
$("#item_rename_file").click(_renameFile);

View File

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

View File

@ -14,29 +14,67 @@ var Octokat = require('octokat');
var test_platform_id = "_TEST";
function newGH(store) {
function newGH(store, platform_id) {
// 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() {
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 gh = newGH(store);
gh.import('https://github.com/sehugg/genemedic/extra/garbage').then( (sess) => {
assert.equal(4, sess.paths.length);
gh.import('https://github.com/pzpinfo/testrepo1557322631070').then( (sess) => {
console.log(sess.paths);
assert.equal(2, sess.paths.length);
// TODO: test for presence in local storage, make sure returns keys
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) {
var store = mstore.createNewPersistentStore(test_platform_id, function(store) {
var gh = newGH(store);
// 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();
});
});
@ -57,11 +95,11 @@ describe('Store', function() {
it('Should bind paths to Github', function(done) {
var store = mstore.createNewPersistentStore(test_platform_id, function(store) {
var gh = newGH(store);
var sess = {prefix:'prefix', url:'_'};
var sess = {prefix:'shared/foo/bar/', url:'_'};
gh.bind(sess, true);
assert.equal(gh.getBoundURL('prefix', '_'));
assert.equal(gh.getBoundURL('shared/foo/bar/'), '_');
gh.bind(sess, false);
assert.equal(gh.getBoundURL('prefix', null));
assert.equal(gh.getBoundURL('shared/foo/bar/'), null);
done();
});
});

View File

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

View File

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