From a58a96062f325c84483439f2d53d84c94f391ec3 Mon Sep 17 00:00:00 2001 From: Justin Fagnani Date: Tue, 26 Apr 2016 16:53:21 -0700 Subject: [PATCH] New init template --- custom_typings/command-line-args.d.ts | 1 + custom_typings/github.d.ts | 96 ++++++++++++++++++ package.json | 8 +- src/commands/init.ts | 16 ++- src/templates/polykart.ts | 134 ++++++++++++++++++++++++++ test/templates/polykart_test.js | 59 ++++++++++++ test/templates/test_tarball.tgz | Bin 0 -> 201 bytes tsconfig.json | 2 + typings.json | 3 +- 9 files changed, 315 insertions(+), 4 deletions(-) create mode 100644 custom_typings/github.d.ts create mode 100644 src/templates/polykart.ts create mode 100644 test/templates/polykart_test.js create mode 100644 test/templates/test_tarball.tgz diff --git a/custom_typings/command-line-args.d.ts b/custom_typings/command-line-args.d.ts index 51c831c3d..88a88de97 100644 --- a/custom_typings/command-line-args.d.ts +++ b/custom_typings/command-line-args.d.ts @@ -10,6 +10,7 @@ declare module 'command-line-args' { defaultValue?: any; type?: Object; multiple?: boolean; + defaultOption?: boolean; } interface UsageOpts { title?: string; diff --git a/custom_typings/github.d.ts b/custom_typings/github.d.ts new file mode 100644 index 000000000..1c4598194 --- /dev/null +++ b/custom_typings/github.d.ts @@ -0,0 +1,96 @@ +declare module "github" { + interface NodeCallback { (err: any, res: T): any; } + interface Options { + version: string; + protocol: string; + } + interface CreatePullRequestOpts { + user: string; + repo: string; + title: string; + head: string; + base: string; + body?: string; + } + interface IssuesEditOpts { + headers?: Object; + user: string; + repo: string; + number: number; + title?: string; + body?: string; + assignee?: string; + milestone?: number; + labels?: string[]; + state?: string; + } + interface GetFromOrgOpts { + org: string; + per_page?: number; + page?: number; + } + interface GetReleaseMessage { + headers?: {string: string}; + owner: string; + repo: string; + id: number; + } + interface ListReleasesMessage { + headers?: {string: string}; + owner: string; + repo: string; + page?: number; + per_page?: number; + } + class GitHubApi { + constructor(options: Options); + repos: { + getFromOrg(msg: GetFromOrgOpts, cb: NodeCallback): void; + get(msg: {user: string, repo: string}, + cb: NodeCallback): void; + } + pullRequests: { + create( + msg: CreatePullRequestOpts, cb: NodeCallback): void; + } + issues: { + edit(msg: IssuesEditOpts, cb: NodeCallback): void; + } + releases: { + getRelease(msg: GetReleaseMessage, cb: NodeCallback): void; + listReleases(msg: ListReleasesMessage, cb: NodeCallback): void; + } + authenticate(credentials: {type: string, token: string}): void; + user: { get(msg: {}, cb: NodeCallback): void; }; + } + namespace GitHubApi { + class Repo { + owner: User; + name: string; + clone_url: string; + } + interface User { + login: string; + } + interface Issue { + number: number; + title: string; + body: string; + assignee: User; + milestone: Milestone; + state: string; + labels: { name: string, color: string, url: string }[]; + user: User; + } + interface PullRequest extends Issue {} + interface Milestone {} + interface Release { + url: string, + name: string, + tag_name: string, + id: number, + tarball_url: string, + } + } + export = GitHubApi; +} diff --git a/package.json b/package.json index b796b523d..5881301c2 100644 --- a/package.json +++ b/package.json @@ -20,14 +20,19 @@ "command-line-commands": "^0.1.2", "dom5": "^1.3.1", "generator-polymer-init": "^0.0.3", + "github": "^0.2.4", "gulp": "^3.9.1", "gulp-if": "^2.0.0", "gulp-vulcanize": "^6.1.0", + "gunzip-maybe": "^1.3.1", "liftoff": "^2.2.1", "minimatch": "^3.0.0", "polylint": "^2.10.0", "polyserve": "^0.10.0", + "request": "^2.72.0", "resolve": "^1.1.7", + "tar-fs": "^1.12.0", + "uglify-js": "^2.6.2", "vinyl": "^1.1.1", "vinyl-fs": "^2.4.3", "web-component-tester": "^4.2.0", @@ -39,6 +44,7 @@ "gulp-mocha": "^2.2.0", "mocha": "^2.4.5", "typescript": "^1.8.10", - "typings": "^0.8.1" + "typings": "^0.8.1", + "yeoman-test": "^1.1.0" } } diff --git a/src/commands/init.ts b/src/commands/init.ts index 4f963cfdb..3a02b34f6 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -9,6 +9,9 @@ */ import {Command} from './command'; +import {ArgDescriptor} from 'command-line-args'; + +import {PolykartGenerator} from '../templates/polykart'; // not ES modules compatible const YeomanEnvironment = require('yeoman-environment'); @@ -23,13 +26,22 @@ export class InitCommand implements Command { description = 'Initializes a Polymer project'; - args = []; + args: ArgDescriptor[] = [ + { + name: 'name', + description: 'The template name', + type: String, + defaultOption: true, + }, + ]; run(options): Promise { return new Promise((resolve, reject) => { + let templateName = options['name'] || 'polymer-init'; let env = new YeomanEnvironment(); env.register(require.resolve('generator-polymer-init'), 'polymer-init:app'); - env.run('polymer-init', {}, () => resolve()); + env.registerStub(PolykartGenerator, 'polykart:app'); + env.run(templateName, {}, () => resolve()); }); } } diff --git a/src/templates/polykart.ts b/src/templates/polykart.ts new file mode 100644 index 000000000..df9587311 --- /dev/null +++ b/src/templates/polykart.ts @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt + */ + +import * as fs from 'fs'; +import * as GitHubApi from 'github'; +import * as http from 'http'; +import {Base} from 'yeoman-generator'; +import * as path from 'path'; +import {Stream} from 'stream'; + +const gunzip = require('gunzip-maybe'); +const request = require('request'); +const tar = require('tar-fs'); + +export const PolykartGenerator = getGenerator(); + +export function getGenerator(options?) { + let realGitHubApi = new GitHubApi({ + version: '3.0.0', + protocol: 'https', + }); + let requestApi = options && options.requestApi || request; + let githubApi = options && options.githubApi || realGitHubApi; + let githubToken = options && options.githubToken; + // let outDir = options && options.outDir || process.cwd(); + + return class PolykartGenerator extends Base { + + _github: GitHubApi; + _githubToken: string; + + constructor(args: string | string[], options: any) { + super(args, options); + this._githubToken = this._getGitHubToken(); + this._github = githubApi; + this._github.authenticate({ + type: 'oauth', + token: this._githubToken, + }); + } + + writing() { + let done = this.async(); + console.log('Finding latest release of polykart...'); + return this._getTemplate() + .then((release) => { + let tarballUrl = release.tarball_url; + console.log('Latest release URL:', tarballUrl); + requestApi({ + url: tarballUrl, + headers: { + 'User-Agent': 'request', + 'Authorization': `token ${this._githubToken}`, + } + }) + .on('response', (response) => { + if (response.statusCode == 200) { + console.log('Downloading template'); + } + }) + .on('end', () => { + console.log('complete'); + done(); + }) + .pipe(gunzip()) + .pipe(tar.extract(this.destinationRoot(), { + ignore: (_, header) => { + let splitPath = header.name.split(path.sep); + // ignore the top directory in the tarfile to unpack directly to + // the cwd + return splitPath.length < 2 || splitPath[1] === ''; + }, + map: (header) => { + let unprefixed = header.name.split(path.sep).slice(1).join(path.sep).trim(); + // the ./ is needed to unpack top-level files in the tar, otherwise + // they're just not written + header.name = './' + unprefixed; + return header; + } + })); + }) + .catch((error) => { + console.error('Could not load polykart template'); + console.error(error); + done(); + }); + } + + install() { + (this).bowerInstall(); + } + + _getGitHubToken() { + if (githubToken) { + return githubToken; + } + try { + return fs.readFileSync('token', 'utf8').trim(); + } catch (e) { + console.error(` + You need to create a github token and place it in a file named 'token'. + The token only needs the 'public repos' permission. + Generate a token here: https://github.com/settings/tokens + This restriction will be removed soon! + `); + } + } + + _getTemplate(): Promise { + return new Promise((resolve, reject) => { + this._github.releases.listReleases({ + owner: 'PolymerLabs', + repo: 'polykart', + }, (err, result) => { + if (err) { + reject(err); + } else { + if (result.length === 0) { + reject('no releases'); + } else { + resolve(result[0]); + } + } + }); + }); + } + } +} diff --git a/test/templates/polykart_test.js b/test/templates/polykart_test.js new file mode 100644 index 000000000..bbe894be9 --- /dev/null +++ b/test/templates/polykart_test.js @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt + */ + +'use strict'; + +const assert = require('chai').assert; +const fs = require('fs'); +const path = require('path'); +const streamLib = require('stream'); +const yoAssert = require('yeoman-assert'); +const helpers = require('yeoman-test'); + +const polykart = require('../../lib/templates/polykart.js'); + +suite('templates/polykart', () => { + + test('untars a release', (done) => { + let mockRequestApi = (options) => { + assert.equal(options.url, 'http://foo.com/bar.tar'); + return fs.createReadStream(path.join(__dirname, 'test_tarball.tgz')); + }; + + let mockGithubApi = { + authenticate(options) { + assert.equal(options.type, 'oauth'); + assert.equal(options.token, 'token-token'); + }, + + releases: { + listReleases(options, cb) { + assert.equal(options.owner, 'PolymerLabs'); + assert.equal(options.repo, 'polykart'); + cb(null, [{ + tarball_url: 'http://foo.com/bar.tar', + }]); + }, + }, + }; + + let generator = new polykart.getGenerator({ + requestApi: mockRequestApi, + githubApi: mockGithubApi, + githubToken: 'token-token', + }); + + helpers.run(generator) + .on('end', (a) => { + yoAssert.file(['file1.txt']); + done(); + }); + }); + +}); diff --git a/test/templates/test_tarball.tgz b/test/templates/test_tarball.tgz new file mode 100644 index 0000000000000000000000000000000000000000..ac9c9acb835a76d9d878bfa03914fa55232bc5c2 GIT binary patch literal 201 zcmb2|=3uDO)C^}}etR*n>yUxKv5z+IHH(<$&fM~{P-S)QN-t6A#Jzh(L%uB(5#u_& z>HmFaA*T~QpXT}>zpWViN$tAxJ(If|<*wx=Dg{klS}xYy_F(P`H_`t$>OW>pofR3F z_0%