├── .gitignore ├── img └── console.png ├── README.md ├── package.json ├── bin └── github-org-overview.js └── lib └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | config.json 3 | -------------------------------------------------------------------------------- /img/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/github-org-overview/master/img/console.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # github-org-overview 2 | 3 | Scans all repos of a github organization and check if published. 4 | 5 | ![](img/console.png) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-org-overview", 3 | "version": "1.1.0", 4 | "description": "Scans all repos of a github organization and check if published", 5 | "main": "lib/index.js", 6 | "bin": { 7 | "github-org-overview": "./bin/github-org-overview.js" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "Tobias Koppers @sokra", 13 | "license": "MIT", 14 | "dependencies": { 15 | "async": "^1.0.0", 16 | "colors": "^1.1.0", 17 | "github": "^0.2.4", 18 | "minimist": "^1.1.1", 19 | "prompt": "^0.2.14", 20 | "supports-color": "^1.3.1" 21 | }, 22 | "devDependencies": {}, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/webpack/github-org-overview.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/webpack/github-org-overview/issues" 29 | }, 30 | "homepage": "https://github.com/webpack/github-org-overview#readme" 31 | } 32 | -------------------------------------------------------------------------------- /bin/github-org-overview.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var path = require("path"); 3 | var fs = require("fs"); 4 | var prompt = require("prompt"); 5 | var argv = require("minimist")(process.argv.slice(2));; 6 | var GHOrgOverview = require("../"); 7 | 8 | var configFile = path.resolve(__dirname, "../config.json"); 9 | 10 | function loadConfiguration(callback) { 11 | fs.exists(configFile, function(exist) { 12 | if(exist && argv.configure) { 13 | var prevConfig = require(configFile); 14 | prompt.get(["org"], function(err, result) { 15 | var config = { 16 | token: prevConfig.token, 17 | org: result.org 18 | }; 19 | fs.writeFile(configFile, JSON.stringify(config), function(err) { 20 | if(err) return callback(err); 21 | callback(null, config); 22 | }); 23 | }); 24 | } else if(exist && !argv.reconfigure) { 25 | console.log("Using configuration, pass --configure to reconfigure org, pass --reconfigure to reconfigure org and token."); 26 | callback(null, require(configFile)); 27 | return; 28 | } else { 29 | prompt.get(["token", "org"], function(err, result) { 30 | var config = { 31 | token: result.token, 32 | org: result.org 33 | }; 34 | fs.writeFile(configFile, JSON.stringify(config), function(err) { 35 | if(err) return callback(err); 36 | callback(null, config); 37 | }); 38 | }); 39 | } 40 | }); 41 | } 42 | 43 | loadConfiguration(function(err, configuration) { 44 | if(err) { 45 | console.error(err); 46 | return; 47 | } 48 | if(argv._[0]) { 49 | configuration.org = argv._[0]; 50 | } 51 | var api = new GHOrgOverview(configuration); 52 | api.load(function(err) { 53 | if(err) { 54 | console.error(err); 55 | return; 56 | } 57 | console.log(api.toString({ 58 | colors: require("supports-color") 59 | })); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var async = require("async"); 2 | var GitHubApi = require("github"); 3 | var readColors = require("colors/safe"); 4 | 5 | function GHOrgOverview(options) { 6 | this.options = options; 7 | this.github = new GitHubApi({ 8 | version: "3.0.0" 9 | }); 10 | } 11 | module.exports = GHOrgOverview; 12 | 13 | function parseLinks(link) { 14 | var re = /<([^>]+)>;\s+rel="([a-z]+)"/g; 15 | var links = {}; 16 | var match; 17 | while(match = re.exec(link)) { 18 | links[match[2]] = match[1]; 19 | } 20 | return links; 21 | } 22 | 23 | function all(api, data, callback) { 24 | data.per_page = 100; 25 | data.page = 1; 26 | var list = []; 27 | api(data, function onResult(err, result) { 28 | if(err) return callback(err); 29 | var links = parseLinks(result.meta.link); 30 | list = list.concat(result); 31 | if(!links.next) { 32 | return callback(null, list); 33 | } 34 | data.page++; 35 | api(data, onResult); 36 | }); 37 | } 38 | 39 | GHOrgOverview.prototype.load = function(callback) { 40 | this.github.authenticate({ 41 | type: "oauth", 42 | token: this.options.token 43 | }); 44 | var org = this.options.org; 45 | all(this.github.repos.getFromOrg, { 46 | org: org 47 | }, function(err, result) { 48 | if(err) return callback(err); 49 | this.repos = result.map(function(repo) { 50 | return { 51 | id: repo.id, 52 | name: repo.name, 53 | fullName: repo.full_name 54 | }; 55 | }); 56 | async.forEach(this.repos, function(repo, callback) { 57 | all(this.github.gitdata.getAllReferences, { 58 | user: org, 59 | repo: repo.name 60 | }, function(err, result) { 61 | if(err) return callback(err); 62 | repo.tags = {}; 63 | repo.heads = {}; 64 | repo.references = result.map(function(ref) { 65 | var s = ref.ref.split("/"); 66 | var sha = ref.object.sha; 67 | return { 68 | type: s[1], 69 | name: s[2], 70 | sha: sha, 71 | objectType: ref.object.type, 72 | ref: ref 73 | }; 74 | }); 75 | async.forEach(repo.references, function(ref, callback) { 76 | if(ref.objectType === "tag") { 77 | this.github.gitdata.getTag({ 78 | user: org, 79 | repo: repo.name, 80 | sha: ref.sha 81 | }, function(err, result) { 82 | if(err) return callback(err); 83 | ref.sha = result.object.sha; 84 | callback(); 85 | }); 86 | } else callback(); 87 | }.bind(this), function(err) { 88 | if(err) return callback(err); 89 | repo.references.forEach(function(ref) { 90 | switch(ref.type) { 91 | case "heads": 92 | repo.heads[ref.name] = ref.sha; 93 | break; 94 | case "tags": 95 | repo.tags[ref.name] = ref.sha; 96 | break; 97 | } 98 | }); 99 | repo.publishedVersion = Object.keys(repo.tags).filter(function(tag) { 100 | return repo.heads.master === repo.tags[tag]; 101 | })[0]; 102 | this.github.gitdata.getCommit({ 103 | user: org, 104 | repo: repo.name, 105 | sha: repo.heads.master 106 | }, function(err, result) { 107 | if(err) return callback(err); 108 | repo.date = result.committer.date; 109 | repo.message = result.message; 110 | callback(); 111 | }); 112 | }.bind(this)); 113 | }.bind(this)); 114 | }.bind(this), function(err) { 115 | if(err) return callback(err); 116 | this.repos.sort(function(a, b) { 117 | if(a.date < b.date) return -1; 118 | if(a.date > b.date) return 1; 119 | return 0; 120 | }); 121 | callback(); 122 | }.bind(this)); 123 | }.bind(this)); 124 | }; 125 | 126 | function identity(str) { return str; } 127 | 128 | GHOrgOverview.prototype.toString = function(options) { 129 | options = options || {}; 130 | var lines = []; 131 | var colors = { 132 | bold: options.colors ? readColors.bold : identity, 133 | green: options.colors ? readColors.green : identity, 134 | red: options.colors ? readColors.red : identity, 135 | yellow: options.colors ? readColors.yellow : identity 136 | }; 137 | lines.push(this.repos.length + " repos:"); 138 | this.repos.forEach(function(repo) { 139 | var line = colors.bold(repo.fullName); 140 | if(repo.publishedVersion) { 141 | line += colors.green(" published as " + repo.publishedVersion); 142 | } else if(Object.keys(repo.tags).length > 0) { 143 | line += colors.red(" not published: " + repo.date + " " + repo.message.split("\n")[0]); 144 | } else { 145 | line += colors.yellow(" no tags: " + repo.date + " " + repo.message.split("\n")[0]); 146 | } 147 | lines.push(line); 148 | }); 149 | return lines.join("\n"); 150 | }; 151 | --------------------------------------------------------------------------------