Last active
July 3, 2017 06:32
-
-
Save chancancode/6d0671b79c23b699838709cfc5521bed to your computer and use it in GitHub Desktop.
New Twiddle
This file contains hidden or 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
import Ember from 'ember'; | |
import { task } from 'ember-concurrency'; | |
const { inject: { service } } = Ember; | |
export default Ember.Controller.extend({ | |
github: service() | |
}); |
This file contains hidden or 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
import Ember from 'ember'; | |
import { task } from 'ember-concurrency'; | |
import { E_BAD_CREDENTIALS, E_BAD_OTP } from '../services/github'; | |
const { computed: { and, equal, not }, inject: { service } } = Ember; | |
export default Ember.Controller.extend({ | |
github: service(), | |
username: null, | |
password: null, | |
otp: null, | |
hasSignIn: and('username', 'password'), | |
disableSignIn: not('hasSignIn'), | |
disableVerify: not('otp'), | |
step: 'credentials', | |
isCredentials: equal('step', 'credentials'), | |
isOTP: equal('step', 'otp'), | |
submit: task(function * () { | |
let { | |
github, username, password, otp | |
} = this.getProperties( | |
'github', 'username', 'password', 'otp' | |
); | |
try { | |
let result = yield github.get('login').perform(username, password, otp); | |
this.setProperties({ | |
username: null, | |
password: null, | |
otp: null, | |
step: 'credentials' | |
}); | |
this.transitionToRoute('index'); | |
} catch(error) { | |
if (error.message === E_BAD_CREDENTIALS) { | |
this.setProperties({ | |
step: 'credentials', | |
password: null, | |
otp: null | |
}); | |
throw new Error('Incorrect username or password.'); | |
} else if (error.message === E_BAD_OTP) { | |
this.setProperties({ | |
step: 'otp', | |
otp: null | |
}); | |
if (otp) { | |
throw new Error('Two-factor authentication failed.'); | |
} else { | |
return; | |
} | |
} | |
throw error; | |
} | |
}).restartable() | |
}); |
This file contains hidden or 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
import Ember from 'ember'; | |
import { task } from 'ember-concurrency'; | |
const { computed, computed: { reads }, inject: { service } } = Ember; | |
export default Ember.Controller.extend({ | |
github: service(), | |
majorVersion: computed('model.finalVersion', function () { | |
let version = this.get('model.finalVersion'); | |
let match = version.match(/^([0-9])\./); | |
if (match) { | |
let [_, major] = match; | |
return parseInt(major, 10); | |
} else { | |
return 3; | |
} | |
}), | |
minorVersion: computed('model.finalVersion', function () { | |
let version = this.get('model.finalVersion'); | |
let match = version.match(/^([0-9])\.([0-9]+)\./); | |
if (match) { | |
let [_, major, minor] = match; | |
return parseInt(minor, 10); | |
} else { | |
return 0; | |
} | |
}), | |
version: computed('majorVersion', 'minorVersion', function () { | |
let { majorVersion, minorVersion } = this.getProperties('majorVersion', 'minorVersion'); | |
return `${majorVersion}.${minorVersion}`; | |
}), | |
betaVersion: computed('majorVersion', 'minorVersion', function () { | |
let { majorVersion, minorVersion } = this.getProperties('majorVersion', 'minorVersion'); | |
return `${majorVersion}.${minorVersion+1}`; | |
}), | |
previousVersion: computed('majorVersion', 'minorVersion', function () { | |
let { majorVersion, minorVersion } = this.getProperties('majorVersion', 'minorVersion'); | |
return `${majorVersion}.${minorVersion-1}`; | |
}), | |
releaseDate: reads('model.cycleEstimatedFinishDate'), | |
title: computed('version', 'betaVersion', function() { | |
let { version, betaVersion } = this.getProperties('version', 'betaVersion'); | |
return `Ember ${version} and ${betaVersion} Beta Released`; | |
}), | |
authors: reads('github.user.name'), | |
pullRequestTitle: computed('version', function() { | |
let version = this.get('version'); | |
return `Ember ${version} Release Blog Post`; | |
}), | |
branch: computed('version', function() { | |
let version = this.get('version'); | |
let name = `${version}-release-blog-post`; | |
return name.toLowerCase().replace(/[^a-z0-9]/g, '-'); | |
}), | |
path: computed('version', function() { | |
let { version, releaseDate } = this.getProperties('version', 'releaseDate'); | |
let filename = `${releaseDate}-ember-${version}-released`; | |
filename = filename.toLowerCase().replace(/[^a-z0-9]/g, '-'); | |
return `source/blog/${filename}.md`; | |
}), | |
emberjsSignOff: '@chancancode', | |
emberDataSignOff: '@bmac', | |
emberCLISignOff: '@rwjblue', | |
websiteSignOff: '@locks, @mixonic', | |
submit: task(function * () { | |
let { | |
github, branch, path, title, authors, version, betaVersion, previousVersion, pullRequestTitle | |
} = this.getProperties( | |
'github', 'branch', 'path', 'title', 'authors', 'version', 'betaVersion', 'previousVersion', 'pullRequestTitle' | |
); | |
try { | |
yield github.get('createBranch').perform('emberjs', 'website', branch); | |
} catch(error) { | |
throw new Error(`Cannot create branch "${path}": ${error.message}`); | |
} | |
try { | |
let content = `--- | |
title: ${title} | |
author: ${authors} | |
tags: Releases | |
--- | |
Today the Ember project is releasing version ${version}.0 of Ember.js, Ember Data, and Ember CLI. | |
This release kicks off the ${betaVersion} beta cycle for all sub-projects. We encourage our | |
community (especially addon authors) to help test these beta builds and report | |
any bugs before they are published as a final release in six weeks' time. The | |
[ember-try](https://github.com/ember-cli/ember-try) addon is a great way to | |
continuously test your projects against the latest Ember releases. | |
You can read more about our general release process here: | |
- [Release Dashboard](http://emberjs.com/builds/) | |
- [The Ember Release Cycle](http://emberjs.com/blog/2013/09/06/new-ember-release-process.html) | |
- [The Ember Project](http://emberjs.com/blog/2015/06/16/ember-project-at-2-0.html) | |
- [Ember LTS Releases](http://emberjs.com/blog/2016/02/25/announcing-embers-first-lts.html) | |
--- | |
## Ember.js | |
Ember.js is the core framework for building ambitious web applications. | |
### Changes in Ember.js ${version} | |
Ember.js ${version} is an incremental, backwards compatible release of Ember with | |
bugfixes, performance improvements, and minor deprecations. | |
#### Deprecations in Ember ${version} | |
Deprecations are added to Ember.js when an API will be removed at a later date. | |
Each deprecation has an entry in the deprecation guide describing the migration | |
path to more stable API. Deprecated public APIs are not removed until a major | |
release of the framework. | |
Consider using the | |
[ember-cli-deprecation-workflow](https://github.com/mixonic/ember-cli-deprecation-workflow) | |
addon if you would like to upgrade your application without immediately addressing | |
deprecations. | |
Two new deprecations are introduces in Ember.js ${version}: | |
* TODO | |
* TODO | |
For more details on changes in Ember.js ${version}, please review the | |
[Ember.js ${version}.0 release page](https://github.com/emberjs/ember.js/releases/tag/v${version}.0). | |
### Upcoming Changes in Ember.js ${betaVersion} | |
Ember.js ${betaVersion} will introduce two new features: | |
* TODO | |
* TODO | |
#### Deprecations in Ember.js ${betaVersion} | |
Two new deprecations are introduces in Ember.js ${betaVersion}: | |
* TODO | |
* TODO | |
For more details on the upcoming changes in Ember.js ${betaVersion}, please review the | |
[Ember.js ${betaVersion}.0-beta.1 release page](https://github.com/emberjs/ember.js/releases/tag/v${betaVersion}.0-beta.1). | |
--- | |
## Ember Data | |
Ember Data is the official data persistence library for Ember.js applications. | |
### Changes in Ember Data ${version} | |
#### Deprecations in Ember Data ${version} | |
Two new deprecations are introduces in Ember Data ${version}: | |
* TODO | |
* TODO | |
For more details on changes in Ember Data ${version}, please review the | |
[Ember Data ${version}.0 release page](https://github.com/emberjs/data/releases/tag/v${version}.0). | |
### Upcoming changes in Ember Data ${betaVersion} | |
#### Deprecations in Ember Data ${betaVersion} | |
For more details on the upcoming changes in Ember Data ${betaVersion}, please review the | |
[Ember Data ${betaVersion}.0-beta.1 release page](https://github.com/emberjs/data/releases/tag/v${betaVersion}.0-beta.1). | |
--- | |
## Ember CLI | |
Ember CLI is the command line interface for managing and packaging Ember.js | |
applications. | |
### Upgrading Ember CLI | |
You may upgrade Ember CLI separately from Ember.js and Ember Data! To upgrade | |
your projects using \`yarn\` run: | |
\`\`\` | |
yarn upgrade ember-cli | |
\`\`\` | |
To upgrade your projects using \`npm\` run: | |
\`\`\` | |
npm install --save-dev ember-cli | |
\`\`\` | |
After running the | |
upgrade command run \`ember init\` inside of the project directory to apply the | |
blueprint changes. You can preview those changes for [applications](https://github.com/ember-cli/ember-new-output/compare/v${previousVersion}.0...v${version}.0) | |
and [addons](https://github.com/ember-cli/ember-addon-output/compare/v${previousVersion}.0...v${version}.0). | |
### Changes in Ember CLI ${version} | |
#### Deprecations in Ember Data ${version} | |
Two new deprecations are introduces in Ember Data ${version}: | |
* TODO | |
* TODO | |
For more details on the changes in Ember CLI ${version} and detailed upgrade | |
instructions, please review the [Ember CLI ${version}.0 release page](https://github.com/ember-cli/ember-cli/releases/tag/v${version}.0). | |
### Upcoming Changes in Ember CLI ${betaVersion} | |
#### Deprecations in Ember CLI ${betaVersion} | |
For more details on the changes in Ember CLI ${betaVersion}.0-beta.1 and detailed upgrade | |
instructions, please review the [Ember CLI ${betaVersion}.0-beta.1 release page](https://github.com/ember-cli/ember-cli/releases/tag/v${betaVersion}.0-beta.1). | |
## Thank You! | |
As a community-driven open-source project with an ambitious scope, each of | |
these releases serve as a reminder that the Ember project would not have been | |
possible without your continued support. We are extremely grateful to our | |
contributors for their efforts.`; | |
yield github.get('createFile').perform('emberjs', 'website', path, pullRequestTitle, content, { branch }); | |
} catch(error) { | |
throw new Error(`Cannot create file "${path}": ${error.message}`); | |
} | |
try { | |
let { | |
emberjsSignOff, emberDataSignOff, emberCLISignOff, websiteSignOff | |
} = this.getProperties( | |
'emberjsSignOff', 'emberDataSignOff', 'emberCLISignOff', 'websiteSignOff' | |
); | |
let content = ` | |
- [Rendered](https://github.com/emberjs/website/blob/${branch}/${path}) | |
- Preview | |
## Sign-offs | |
- [ ] Ember.js (${emberjsSignOff}) | |
- [ ] Ember Data (${emberDataSignOff}) | |
- [ ] Ember CLI (${emberCLISignOff}) | |
- [ ] Website (${websiteSignOff}) | |
## Checklist | |
- Ember | |
- [ ] Blog | |
- [ ] v${version}.0 | |
- [ ] v${betaVersion}.0-beta.1 | |
- Ember Data | |
- [ ] Blog | |
- [ ] v${version}.0 | |
- [ ] v${betaVersion}.0-beta.1 | |
- Ember CLI | |
- [ ] Blog | |
- [ ] v${version}.0 | |
- [ ] v${betaVersion}.0-beta.1 | |
See https://github.com/emberjs/website/pull/2824/files for inspiration.`; | |
let url = yield github.get('createPullRequest').perform('emberjs', 'website', pullRequestTitle, branch, 'master', content); | |
return url; | |
} catch(error) { | |
throw new Error(`Cannot create pull request "${title}": ${error.message}`); | |
} | |
}).restartable() | |
}); |
This file contains hidden or 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
import Ember from 'ember'; | |
import config from './config/environment'; | |
const Router = Ember.Router.extend({ | |
location: 'none', | |
rootURL: config.rootURL | |
}); | |
Router.map(function() { | |
this.route('login'); | |
this.route('release-blog-post'); | |
}); | |
export default Router; |
This file contains hidden or 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
import Ember from 'ember'; | |
const { inject: { service } } = Ember; | |
export default Ember.Route.extend({ | |
github: service(), | |
async beforeModel() { | |
try { | |
await this.get('github.validateToken').perform(); | |
} catch(error) { | |
this.replaceWith('login'); | |
} | |
} | |
}); |
This file contains hidden or 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
import Ember from 'ember'; | |
const { inject: { service } } = Ember; | |
export default Ember.Route.extend({ | |
github: service(), | |
async model() { | |
let content = await this.get('github.readFile').perform('ember-learn', 'builds', 'app/fixtures/ember/beta.js'); | |
content = content.replace('export default', 'return'); | |
return eval(`(function() {${content}})()`); | |
} | |
}); |
This file contains hidden or 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
import Ember from 'ember'; | |
import { isUnauthorizedError } from 'ember-ajax/errors'; | |
import { task } from 'ember-concurrency'; | |
const { inject: { service } } = Ember; | |
const TOKEN_NAME = 'Ember Core Dashboard'; | |
const GITHUB_HOST = 'https://api.github.com'; | |
const GITHUB_HEADERS = Object.freeze({ | |
'Content-Type': 'application/vnd.github.v3+json' | |
}); | |
export const E_BAD_CREDENTIALS = 'BAD CREDENTIALS'; | |
export const E_BAD_OTP = 'BAD OTP'; | |
export const E_BAD_TOKEN = 'BAD TOKEN'; | |
export default Ember.Service.extend({ | |
ajax: service(), | |
token: '739daa7d3f65f4753582d4a5d063396fd82c933a', // Hardcode a token here to make development easier | |
user: null, | |
login: task(function * (username, password, otp) { | |
this.setProperties({ | |
token: null, | |
user: null | |
}); | |
let ajax = this.get('ajax'); | |
try { | |
let url = `${GITHUB_HOST}/authorizations`; | |
let auth = btoa(`${username}:${password}`); | |
let headers = { | |
'Authorization': `Basic ${auth}`, | |
'X-GitHub-OTP': otp, | |
...GITHUB_HEADERS | |
}; | |
while (true) { | |
let response = yield ajax.raw(url, { headers }); | |
let token = response.payload.find(t => t.app.name === TOKEN_NAME); | |
if (token) { | |
url = `${GITHUB_HOST}/authorizations/${token.id}`; | |
yield ajax.raw(url, { | |
method: 'DELETE', | |
headers | |
}); | |
break; | |
} | |
let match = response.jqXHR.getResponseHeader("Link").match(/<(.+)>; rel="next"/); | |
if (match) { | |
url = match[1]; | |
} else { | |
break; | |
} | |
} | |
url = `${GITHUB_HOST}/authorizations`; | |
let response = yield ajax.raw(url, { | |
method: 'POST', | |
headers, | |
data: JSON.stringify({ | |
scopes: ['repo'], | |
note: 'Ember Core Dashboard' | |
}) | |
}); | |
this.set('token', response.payload.token); | |
return this.get('validateToken').perform(); | |
} catch(error) { | |
if (isUnauthorizedError(error.response)) { | |
if (error.jqXHR.getResponseHeader('X-GitHub-OTP')) { | |
throw new Error(E_BAD_OTP); | |
} else { | |
throw new Error(E_BAD_CREDENTIALS); | |
} | |
} | |
throw error; | |
} | |
}).restartable(), | |
validateToken: task(function * () { | |
let { ajax, token } = this.getProperties('ajax', 'token'); | |
try { | |
if (!token) { | |
throw new Error('Missing token'); | |
} | |
let url = `${GITHUB_HOST}/user`; | |
let headers = { | |
'Authorization': `token ${token}`, | |
...GITHUB_HEADERS | |
}; | |
let user = yield ajax.request(url, { headers }); | |
this.set('user', user); | |
return true; | |
} catch(error) { | |
this.set('token', null); | |
throw new Error(E_BAD_TOKEN); | |
} | |
}).restartable(), | |
createBranch: task(function * (owner, repo, branch, { base = 'master' } = {}) { | |
let { ajax, token } = this.getProperties('ajax', 'token'); | |
let url = `${GITHUB_HOST}/repos/${owner}/${repo}/git/refs/heads/${base}`; | |
let headers = { | |
'Authorization': `token ${token}`, | |
...GITHUB_HEADERS | |
}; | |
let { object: { sha } } = yield ajax.request(url, { headers }); | |
url = `${GITHUB_HOST}/repos/${owner}/${repo}/git/refs`; | |
return ajax.request(url, { | |
method: 'POST', | |
headers, | |
data: JSON.stringify({ | |
ref: `refs/heads/${branch}`, | |
sha | |
}) | |
}); | |
}), | |
readFile: task(function * (owner, repo, path) { | |
let { ajax, token } = this.getProperties('ajax', 'token'); | |
let url = `${GITHUB_HOST}/repos/${owner}/${repo}/contents/${path}`; | |
let headers = { | |
'Authorization': `token ${token}`, | |
...GITHUB_HEADERS | |
}; | |
let response = yield ajax.request(url, { headers }); | |
return atob(response.content); | |
}), | |
createFile: task(function * (owner, repo, path, message, content, { branch } = {}) { | |
let { ajax, token } = this.getProperties('ajax', 'token'); | |
let url = `${GITHUB_HOST}/repos/${owner}/${repo}/contents/${path}`; | |
let headers = { | |
'Authorization': `token ${token}`, | |
...GITHUB_HEADERS | |
}; | |
return ajax.request(url, { | |
method: 'PUT', | |
headers, | |
data: JSON.stringify({ | |
path, | |
message, | |
branch, | |
content: btoa(content) | |
}) | |
}); | |
}), | |
createPullRequest: task(function * (owner, repo, title, head, base, body) { | |
let { ajax, token } = this.getProperties('ajax', 'token'); | |
let url = `${GITHUB_HOST}/repos/${owner}/${repo}/pulls`; | |
let headers = { | |
'Authorization': `token ${token}`, | |
...GITHUB_HEADERS | |
}; | |
return ajax.request(url, { | |
method: 'POST', | |
headers, | |
data: JSON.stringify({ | |
title, | |
head, | |
base, | |
body | |
}) | |
}); | |
}), | |
}); |
This file contains hidden or 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
* { | |
box-sizing: border-box; | |
} | |
body { | |
margin: 12px 16px; | |
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; | |
font-size: 12pt; | |
color: #333; | |
background: #f9f9f9; | |
} | |
h1 { | |
margin: 32px 0 24px; | |
color: #999; | |
font-weight: lighter; | |
font-size: 12px; | |
text-align: center; | |
} | |
h2 { | |
margin: 0.67em 0; | |
font-weight: 300; | |
font-size: 24px; | |
text-align: center; | |
} | |
a { | |
color: #0366d6; | |
text-decoration: none; | |
} | |
a:hover, a:active { | |
text-decoration: underline; | |
outline-width: 0; | |
} | |
.narrow { | |
width: 340px; | |
margin: auto; | |
} | |
.success, .error { | |
padding: 15px 20px; | |
margin: 0 auto; | |
margin-bottom: 10px; | |
font-size: 13px; | |
border-style: solid; | |
border-width: 1px; | |
border-radius: 5px; | |
} | |
.success { | |
color: #165c26; | |
background-color: #dcffe4; | |
border-color: rgba(27,31,35,0.15); | |
} | |
.error { | |
color: #86181d; | |
background-color: #ffdce0; | |
border: 1px solid rgba(27,31,35,0.15); | |
} | |
.box { | |
background-color: #fff; | |
border: 1px solid #d8dee2; | |
border-radius: 5px; | |
padding: 20px; | |
font-size: 14px; | |
} | |
label { | |
display: block; | |
font-weight: 600; | |
} | |
input { | |
display: block; | |
width: 100%; | |
margin-top: 7px; | |
margin-bottom: 15px; | |
padding: 6px 8px; | |
font-size: 14px; | |
line-height: 20px; | |
color: #24292e; | |
vertical-align: middle; | |
border: 1px solid #d1d5da; | |
border-radius: 3px; | |
outline: none; | |
box-shadow: inset 0 1px 2px rgba(27,31,35,0.075); | |
} | |
input:focus { | |
border-color: #2188ff; | |
outline: none; | |
box-shadow: inset 0 1px 2px rgba(27,31,35,0.075), 0 0 0 0.2em rgba(3,102,214,0.3); | |
} | |
button { | |
display: block; | |
width: 100%; | |
text-align: center; | |
padding: 6px 12px; | |
font-size: 14px; | |
font-weight: 600; | |
line-height: 20px; | |
white-space: nowrap; | |
vertical-align: middle; | |
cursor: pointer; | |
user-select: none; | |
background-repeat: repeat-x; | |
background-position: -1px -1px; | |
background-size: 110% 110%; | |
border: 1px solid rgba(27,31,35,0.2); | |
border-radius: 0.25em; | |
outline: none; | |
-webkit-appearance: none; | |
-moz-appearance: none; | |
appearance: none; | |
margin: 6px 0px; | |
color: #24292e; | |
background-color: #eff3f6; | |
background-image: linear-gradient(-180deg, #fafbfc 0%, #eff3f6 90%); | |
} | |
button:focus { | |
box-shadow: 0 0 0 0.2em rgba(3,102,214,0.3); | |
} | |
button:disabled { | |
color: rgba(36,41,46,0.4); | |
background-color: #eff3f6; | |
background-image: none; | |
border-color: rgba(27,31,35,0.2); | |
box-shadow: none; | |
cursor: default; | |
} | |
button.primary { | |
margin-top: 20px; | |
color: #fff; | |
background-color: #28a745; | |
background-image: linear-gradient(-180deg, #34d058 0%, #28a745 90%); | |
} | |
button.primary:focus { | |
box-shadow: 0 0 0 0.2em rgba(52,208,88,0.3); | |
} | |
button.primary:disabled { | |
color: rgba(255,255,255,0.75); | |
background-color: #94d3a2; | |
background-image: none; | |
border-color: rgba(27,31,35,0.2); | |
box-shadow: none; | |
} | |
button.danger { | |
color: #cb2431; | |
background-color: #fafbfc; | |
} |
This file contains hidden or 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
{ | |
"version": "0.12.1", | |
"EmberENV": { | |
"FEATURES": {} | |
}, | |
"options": { | |
"use_pods": false, | |
"enable-testing": false | |
}, | |
"dependencies": { | |
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js", | |
"ember": "2.12.0", | |
"ember-template-compiler": "2.12.0", | |
"ember-testing": "2.12.0" | |
}, | |
"addons": { | |
"ember-ajax": "3.0.0", | |
"ember-concurrency": "0.8.7", | |
"ember-data": "2.12.1" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment