mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-02-16 17:30:27 +00:00
write README when creating github repository, check platform, migrate files
This commit is contained in:
parent
806687c31d
commit
4cc9aaeaca
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
90
src/ui.ts
90
src/ui.ts
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user