├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── assets ├── demo.gif ├── demo.mov ├── screenshot1.png └── screenshot2.png ├── bin ├── announce ├── assets │ ├── test-anchors │ ├── test-greedy │ ├── test-no-links │ ├── test-no-redirects │ ├── test-readme │ ├── test-scan │ └── test-white-list ├── comments ├── frankenstein ├── issues ├── mergeclose ├── new ├── review ├── scan └── todo ├── frankenstein.gemspec └── lib ├── frankenstein.rb └── frankenstein ├── announce.rb ├── cli.rb ├── comments.rb ├── constants.rb ├── core.rb ├── date.rb ├── diff.rb ├── emoji.rb ├── github.rb ├── io.rb ├── issues.rb ├── log.rb ├── merge.rb ├── network.rb ├── new.rb ├── output.rb ├── readmes.rb ├── review.rb ├── scan.rb ├── todo.rb ├── twitter.rb ├── usage.rb ├── version.rb └── whitelist.rb /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | bin/logs/* 3 | bin/assets/awesome 4 | *.gem 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2 4 | before_script: 5 | - rake install 6 | script: 7 | - frankenstein 8 | - frankenstein bin/assets/test-readme -v threads=0 silent 9 | - frankenstein bin/assets/test-no-links -v silent 10 | - frankenstein bin/assets/test-white-list -mv head silent 11 | - frankenstein fastlane/fastlane.tools -v threads=8 silent 12 | - frankenstein https://github.com/dkhamsing/frankenstein -v threads=11 silent 13 | - frankenstein bin/assets/test-no-redirects -v 14 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in frankenstein.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frankenstein 2 | 3 | `frankenstein` checks for live URLs in a file, it can [update links based on HTTP redirects](#correct-github-readme-redirects) in a README :octocat: 4 | 5 | ![](http://i.giphy.com/2MMB4JT8lokbS.gif) 6 | 7 | This project uses [`awesome_bot`](https://github.com/dkhamsing/awesome_bot) to validate links. 8 | 9 | [![Build Status](https://travis-ci.org/dkhamsing/frankenstein.svg)](https://travis-ci.org/dkhamsing/frankenstein) 10 | 11 | ## Installation 12 | 13 | ```shell 14 | git clone https://github.com/dkhamsing/frankenstein.git 15 | cd frankenstein 16 | rake install 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```shell 22 | $ frankenstein https://fastlane.tools # URL 23 | $ frankenstein README.md # Path to file 24 | $ frankenstein ccgus/fmdb # GitHub repo README, https://github.com/ccgus/fmdb works too 25 | 26 | Found: master for ccgus/fmdb — A Cocoa / Objective-C wrapper around SQLite — 8935⭐️ — last updated today 27 | 🏃 Processing links for ccgus/fmdb ... 28 | 🔎 Checking 18 links 29 | ✅ https://www.zetetic.net/sqlcipher/ 30 | ✅ http://sqlite.org/ 31 | ✅ https://cocoapods.org/ 32 | ✅ https://github.com/marcoarment/FCModel 33 | ✅ https://github.com/layerhq/FMDBMigrationManager 34 | #... 35 | 🕐 Time elapsed: 4.07 seconds 36 | 37 | 🏃 No failures for ccgus/fmdb 38 | ``` 39 | 40 | ``` 41 | ✅ 200 ok 42 | 🔶 3xx redirect 43 | 🔴 4xx error 44 | ⚪ white list / other 45 | ``` 46 | 47 | ### Correct GitHub README Redirects 48 | 49 | `frankenstein` can open a pull request to update README links based on HTTP redirects (this requires credentials set in [.netrc](http://octokit.github.io/octokit.rb/#Using_a__netrc_file)). 50 | 51 | ```shell 52 | $ frankenstein fastlane/sigh 53 | 54 | Finding default branch for fastlane/sigh 55 | Found: master for fastlane/sigh — Because you would rather spend your time building stuff than fighting provisioning — 864⭐️ — last updated 8 days ago 56 | 🏃 Processing links for https://raw.githubusercontent.com/fastlane/sigh/master/README.md ... 57 | 🔎 Checking 21 links 58 | 🔶 301 https://github.com/KrauseFx/fastlane 59 | 🔶 301 https://github.com/KrauseFx/deliver 60 | #... 61 | 🔶 10 redirects 62 | https://github.com/KrauseFx/fastlane redirects to 63 | https://github.com/fastlane/fastlane 64 | #... 65 | Next? (pull request | white list w= | gist | tweet [-h] [message] | enter to end) p 66 | Creating pull request on GitHub for fastlane/sigh ... 67 | Pull request created: https://github.com/fastlane/sigh/pull/195 68 | ``` 69 | 70 | ### White List 71 | 72 | - URLs that are meant to be redirected (i.e. URL shortener, badge, authentication) are [white listed](lib/frankenstein/whitelist.rb) and not corrected. 73 | 74 | - You can also white list links at the end of a run with option `w`. 75 | 76 | ```shell 77 | $ frankenstein dkhamsing/forker 78 | 79 | Finding default branch for dkhamsing/forker 80 | Found: wip for dkhamsing/forker — Fork GitHub repos found on a page — 0⭐️ — last updated today 81 | 🏃 Processing links for dkhamsing/forker ... 82 | 🔎 Checking 10 links 83 | ✅ https://github.com/opensourceios 84 | #... 85 | 🔶 1 redirect 86 | http://gph.is/1768v38 redirects to 87 | http://giphy.com/gifs/loop-factory-how-its-made-n1JN4fSrXovJe 88 | #... 89 | 🕐 Time elapsed: 2.56 seconds 90 | 91 | 🏃 No failures for dkhamsing/forker 92 | 93 | Next? (pull request | white list w= | gist | tweet [-h] [message] | enter to end) w=gph 94 | #... 95 | ``` 96 | 97 | ## Contact 98 | 99 | - [github.com/dkhamsing](https://github.com/dkhamsing) 100 | - [twitter.com/dkhamsing](https://twitter.com/dkhamsing) 101 | 102 | ## License 103 | 104 | This project is available under the MIT license. See the [LICENSE](LICENSE) file for more info. 105 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | -------------------------------------------------------------------------------- /assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkhamsing/frankenstein/27b360d79760686e3c72eafd68f6bdee378023ce/assets/demo.gif -------------------------------------------------------------------------------- /assets/demo.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkhamsing/frankenstein/27b360d79760686e3c72eafd68f6bdee378023ce/assets/demo.mov -------------------------------------------------------------------------------- /assets/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkhamsing/frankenstein/27b360d79760686e3c72eafd68f6bdee378023ce/assets/screenshot1.png -------------------------------------------------------------------------------- /assets/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkhamsing/frankenstein/27b360d79760686e3c72eafd68f6bdee378023ce/assets/screenshot2.png -------------------------------------------------------------------------------- /bin/announce: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.push File.expand_path('../../lib', __FILE__) 3 | 4 | require 'frankenstein/announce' 5 | Announce 6 | -------------------------------------------------------------------------------- /bin/assets/test-anchors: -------------------------------------------------------------------------------- 1 | https://github.com/rwldrn/idiomatic.js#readme 2 | https://github.com/dkhamsing/frankenstein 3 | https://github.com/supermarin/Alcatraz 4 | -------------------------------------------------------------------------------- /bin/assets/test-greedy: -------------------------------------------------------------------------------- 1 | Welcome to RegExr v2.0 by gskinner.com! 2 | 3 | Edit the Expression & Text to see matches. Roll over matches or the expression for details. Undo mistakes with cmd-z. Save & Share expressions with friends or the Community. A full Reference & Help is available in the Library, or watch the video Tutorial. 4 | 5 | Sample text for testing: 6 | abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 7 | 0123456789 +-.,!@#$%^&*();\/|<>"' 8 | 12345 -98.7 3.141 .6180 9,000 +42 9 | 555.123.4567 +1-(800)-555-2468 10 | foo@demo.net bar.ba@test.co.uk 11 | www.demo.com http://foo.co.uk/ 12 | http://regexr.com/foo.html?q=bar 13 | 14 | http://cocoapods.org/ 15 | http://cocoapods.org 16 | 17 | text 18 | text 19 | -------------------------------------------------------------------------------- /bin/assets/test-no-links: -------------------------------------------------------------------------------- 1 | # no links here :-) 2 | -------------------------------------------------------------------------------- /bin/assets/test-no-redirects: -------------------------------------------------------------------------------- 1 | http://code.google.com/speed/page-speed/download.html#pagespeed-sdk 2 | 3 | -------------------------------------------------------------------------------- /bin/assets/test-readme: -------------------------------------------------------------------------------- 1 | # test-readme 2 | 3 | https://github.com/dkhamsing/frankenstein 4 | https://github.com/fastlane/fastlane 5 | 6 | fastlane 7 | fastlane 8 | fastlane 9 | 10 | https://github.com/dkhamsing, https://twitter.com/dkhamsing 11 | 12 | ## redirects 13 | 14 | - http://fontawesome.io/ 15 | - http://atlas.layer.com/ 16 | -------------------------------------------------------------------------------- /bin/assets/test-scan: -------------------------------------------------------------------------------- 1 | [frankenstein] 2 | 3 | 4 | 5 | [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome) 6 | 7 | A curated list of awesome iOS frameworks, libraries, tutorials, plugins Xcode, components and much more. 8 | The list is divided into categories such as Frameworks, Components, Testing and others, open source projects, free and paid services. There is no pre-established order of items in each category, the order is for contribution. If you want to contribute, please read the [guide](https://github.com/vsouza/awesome-ios/blob/master/CONTRIBUTING.md). 9 | 10 | Projects in Swift language will be marked with :large_orange_diamond: and :watch: for Apple Watch projects. Feel free to add your project. 11 | 12 | ### Contents 13 | - [Getting Started](#getting-started) 14 | - [Library and Frameworks](#libraries-and-frameworks) 15 | - [Audio](#audio) 16 | - [Animation](#animation) 17 | - [Bridging](#bridging) 18 | - [Cache](#cache) 19 | - [Core Data](#core-data) 20 | - [Charts](#charts) 21 | - [Database](#database) 22 | - [Hardware](#hardware) 23 | - [Motion](#motion) 24 | - [Bluetooth](#bluetooth) 25 | - [Location](#location) 26 | - [iBeacon](#ibeacon) 27 | - [HUD](#hud) 28 | - [EventBus](#eventbus) 29 | - [Files](#files) 30 | - [JSON](#json) 31 | - [Layout](#layout) 32 | - [Logging](#logging) 33 | - [Maps](#maps) 34 | - [Media](#media) 35 | - [Image](#image) 36 | - [Video](#video) 37 | - [PDF](#pdf) 38 | - [Messaging](#messaging) 39 | - [Networking](#networking) 40 | - [Push Notifications](#push-notifications) 41 | - [Passbook](#passbook) 42 | - [Permissions](#permissions) 43 | - [Text](#text) 44 | - [Walkthrough / Intro / Tutorial](#walkthrough--intro--tutorial) 45 | - [URL Scheme](#url-scheme) 46 | - [UI](#ui) 47 | - [Websocket](#websocket) 48 | - [Code Quality](#code-quality) 49 | - [Analytics](#analytics) 50 | - [Payments](#payments) 51 | - [Products](#products) 52 | - [Utility](#utility) 53 | - [Security](#security) 54 | 55 | - [Project setup](#project-setup) 56 | - [Dependency / Package Manager](#dependency--package-manager) 57 | - [Testing](#testing) 58 | - [TDD / BDD](#tdd--bdd) 59 | - [UI Testing](#ui-testing) 60 | - [Beta Distribution](#beta-distribution) 61 | - [Other Testing](#other-testing) 62 | - [Toolchains](#toolchains) 63 | - [Tools](#tools) 64 | - [Rapid Development](#rapid-development) 65 | - [Deployment](#deployment) 66 | - [App Store](#app-store) 67 | - [SDK](#sdk) 68 | - [Xcode](#xcode) 69 | - [Plugins](#plugins) 70 | - [Themes](#themes) 71 | - [Other Xcode](#other-xcode) 72 | - [Style Guides](#style-guides) 73 | - [Good Websites](#good-websites) 74 | - [News, Blogs and Feeds](#news-blogs-and-feeds) 75 | - [UIKIt references](#uikit-references) 76 | - [Forums and discuss lists](#forums-and-discuss-lists) 77 | - [Tutorials and Keynotes](#tutorials-and-keynotes) 78 | - [Prototyping](#prototyping) 79 | - [Twitter](#twitter) 80 | - [Facebook Groups](#facebook-groups) 81 | - [Podcasts](#podcasts) 82 | - [Books](#books) 83 | - [Other Awesome Lists](#other-awesome-lists) 84 | - [Contributing](#contributing) 85 | 86 | # Getting Started 87 | * [Road Map iOS](https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/DevelopiOSAppsSwift/) - Start Developing iOS Apps Today, Apple Guide. :large_orange_diamond: 88 | * [Lifehacker](http://lifehacker.com/i-want-to-write-ios-apps-where-do-i-start-1644802175) - I Want to Write iOS Apps. Where Do I Start? 89 | * [Codeproject](http://www.codeproject.com/Articles/88929/Getting-Started-with-iPhone-and-iOS-Development) - Getting Started with iPhone and iOS Development. 90 | * [Ray Wenderlich](http://www.raywenderlich.com/38557/learn-to-code-ios-apps-1-welcome-to-programming) - Learn to code iOS Apps. 91 | * [Stanford - Developing Apps to iOS](https://itunes.apple.com/us/itunes-u/developing-apps-for-ios-hd/id395605774?mt=10) - Stanford's iTunes U App Development Course (Audio and Video). 92 | * [Stanford - Developing iOS 8 Apps with Swift](https://itunes.apple.com/us/course/developing-ios-8-apps-swift/id961180099) - Stanford's 2015 iTunes U App Development Course. :large_orange_diamond: 93 | 94 | # Libraries And Frameworks 95 | 96 | ### Audio 97 | * [AudioBus](https://developer.audiob.us/) - Add Next Generation Live App-to-App Audio Routing 98 | * [AudioKit](https://github.com/audiokit/AudioKit) - A powerful toolkit for synthesizing, processing, and analyzing sounds. 99 | * [EZAudio](https://github.com/syedhali/EZAudio) - An iOS/OSX audio visualization framework built upon Core Audio useful for anyone doing real-time, low-latency audio processing and visualizations. 100 | * [novocaine](https://github.com/alexbw/novocaine) - Painless high-performance audio on iOS and Mac OS X. 101 | * [QHSpeechSynthesizerQueue](https://github.com/quentinhayot/QHSpeechSynthesizerQueue) - Queue management system for AVSpeechSynthesizer (iOS Text to Speech). 102 | * [StreamingKit](https://github.com/tumtumtum/StreamingKit) - A fast and extensible gapless AudioPlayer/AudioStreamer for OSX and iOS. 103 | 104 | ### Animation 105 | * [Pop](https://github.com/facebook/pop) - An extensible iOS and OS X animation library, useful for physics-based interactions. 106 | * [AnimationEngine](https://github.com/intuit/AnimationEngine) - Easily build advanced custom animations on iOS. 107 | * [Awesome-iOS-Animation](https://github.com/jackyzh/awesome-ios-animation) - Collection of Animation projects 108 | * [RZTransitions](https://github.com/Raizlabs/RZTransitions) - A library of custom iOS View Controller Animations and Interactions. 109 | * [DCAnimationKit](https://github.com/daltoniam/DCAnimationKit) - A collection of animations for iOS. Simple, just add water animations. 110 | * [Spring](https://github.com/MengTo/Spring) - A library to simplify iOS animations in Swift. 111 | * [Canvas](https://github.com/CanvasPod/Canvas) - Animate in Xcode without code http://canvaspod.io 112 | 113 | ### Bridging 114 | * [JSPatch](https://github.com/bang590/JSPatch) - JSPatch bridge Objective-C and Javascript using the Objective-C runtime. You can call any Objective-C class and method in JavaScript by just including a small engine. JSPatch is generally use for hotfix iOS App. 115 | 116 | ### Cache 117 | * [SDURLCache](https://github.com/steipete/SDURLCache) - URLCache subclass with on-disk cache support on iPhone/iPad. 118 | * [Awesome Cache](https://github.com/aschuch/AwesomeCache) - Delightful on-disk cache :large_orange_diamond: 119 | * [mattress](https://github.com/buzzfeed/mattress) - iOS Offline Caching for Web Content :large_orange_diamond: 120 | 121 | ### Charts 122 | * [ios-charts](https://github.com/danielgindi/ios-charts) - A powerful chart / graph framework, the iOS equivalent to [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart). :large_orange_diamond: 123 | * [JTChartView](https://github.com/kubatru/JTChartView) 124 | * [PNChart](https://github.com/kevinzhow/PNChart) - A simple and beautiful chart lib used in Piner and CoinsMan for iOS 125 | * [BEMSimpleLineGraph](https://github.com/Boris-Em/BEMSimpleLineGraph) - Elegant Line Graphs for iOS (charting library). 126 | * [JBChartView](https://github.com/Jawbone/JBChartView) - iOS-based charting library for both line and bar graphs. 127 | * [iOSPlot](https://github.com/honcheng/iOSPlot) - Chart library for iOS. 128 | * [XYPieChart](https://github.com/xyfeng/XYPieChart) - A simple and animated Pie Chart for your iOS app. 129 | * [TEAChart](https://github.com/xhacker/TEAChart) - Simple and intuitive iOS chart library. Contribution graph, clock chart, and bar chart. 130 | * [EChart](https://github.com/zhuhuihuihui/EChart) - iOS/iPhone/iPad Chart, Graph. Event handling and animation supported. 131 | * [FSLineChart](https://github.com/ArthurGuibert/FSLineChart) - A line chart library for iOS. 132 | * [chartee](https://github.com/zhiyu/chartee) - a charting library for mobile platforms. 133 | * [ANDLineChartView](https://github.com/anaglik/ANDLineChartView) - ANDLineChartView is easy to use view-based class for displaying animated line chart. 134 | * [TWRCharts](https://github.com/chasseurmic/TWRCharts) - An iOS wrapper for ChartJS. Easily build animated charts by leveraging the power of native Obj-C code. 135 | 136 | ### Core Data 137 | * [CWCoreData](https://github.com/jayway/CWCoreData) - Additions and utilities to make it concurrency easier with the Core Data framework. 138 | * [ObjectiveRecord](https://github.com/supermarin/ObjectiveRecord) - ActiveRecord for Objective-C. 139 | * [SSDataKit](https://github.com/soffes/SSDataKit) - Eliminate your Core Data boilerplate code. 140 | * [ios-queryable](https://github.com/martydill/ios-queryable) - ios-queryable is an implementation of IQueryable/IEnumerable for Core Data. 141 | * [ReactiveCoreData](https://github.com/apparentsoft/ReactiveCoreData) - ReactiveCoreData (RCD) is an attempt to bring Core Data into the ReactiveCocoa (RAC) world. 142 | * [Ensembles](https://github.com/drewmccormack/ensembles) - A synchronization framework for Core Data. 143 | * [SLRESTfulCoreData](https://github.com/OliverLetterer/SLRESTfulCoreData) - Objc naming conventions, autogenerated accessors at runtime, URL substitutions and intelligent attribute mapping. 144 | * [Mogenerator](https://github.com/rentzsch/mogenerator) - Automatic Core Data code generation. 145 | * [HardCoreData](https://github.com/Krivoblotsky/HardCoreData) - CoreData stack and controller that will never block UI thread. 146 | * [encrypted-core-data](https://github.com/project-imas/encrypted-core-data) - Core Data encrypted SQLite store using SQLCipher. 147 | * [MagicalRecord](https://github.com/magicalpanda/MagicalRecord) - Super Awesome Easy Fetching for Core Data. 148 | * [QueryKit](https://github.com/QueryKit/QueryKit) - A simple type-safe Core Data query language. :large_orange_diamond: 149 | * [CoreStore](https://github.com/JohnEstropia/CoreStore) - Powerful Core Data framework for Incremental Migrations, Fetching, Observering, etc. :large_orange_diamond: 150 | 151 | ### Database 152 | * [Realm](https://github.com/realm/realm-cocoa) - The alternative to CoreData and SQLite: Simple, modern and fast. 153 | * [YapDatabase](https://github.com/yapstudios/YapDatabase) - YapDatabase is an extensible database for iOS & Mac. 154 | * [Couchbase Mobile](http://developer.couchbase.com/mobile/) - Couchbase document store for mobile with cloud sync. 155 | * [FMDB](https://github.com/ccgus/fmdb) - A Cocoa / Objective-C wrapper around SQLite. 156 | * [Akaibu-NSUserDefaults](https://github.com/roytang121/Akaibu-NSUserDefaults) - a Swifty Key-value store for archiving NSObject in only one line of code. Class properties are automatically mapped and archived under the hood. 157 | * [FCModel](https://github.com/marcoarment/FCModel) - An alternative to Core Data for people who like having direct SQL access. 158 | 159 | ### Encryption 160 | * [AESCrypt-ObjC](https://github.com/Gurpartap/AESCrypt-ObjC) - A simple and opinionated AES encrypt / decrypt Objective-C class that just works. 161 | 162 | ### Hardware 163 | ##### Motion 164 | * [MotionKit](https://github.com/MHaroonBaig/MotionKit) - Get the data from Accelerometer, Gyroscope and Magnetometer in only Two or a few lines of code. CoreMotion now made insanely simple. 165 | 166 | ##### Bluetooth 167 | * [Discovery](https://github.com/omergul123/Discovery) - A very simple library to discover and retrieve data from nearby devices (even if the peer app works at background). 168 | * [LGBluetooth](https://github.com/l0gg3r/LGBluetooth) - Simple, block-based, lightweight library over CoreBluetooth. Will clean up your Core Bluetooth related code. 169 | * [PeerKit](https://github.com/jpsim/PeerKit) An open-source Swift framework for building event-driven, zero-config Multipeer Connectivity apps. :large_orange_diamond: 170 | * [simple-share](https://github.com/lauraskelton/simple-share) - Easy Proximity-based Bluetooth LE Sharing for iOS. 171 | * [BluetoothKit](https://github.com/rasmusth/BluetoothKit) - Easily communicate between iOS/OSX devices using BLE. :large_orange_diamond: 172 | 173 | ##### Location 174 | * [IngeoSDK](https://github.com/IngeoSDK/ingeo-ios-sdk) - Always-On Location monitoring framework for iOS. 175 | * [Proxitee](https://github.com/Proxitee/iOS-SDK) - Allows developers to create proximity aware applications utilizing iBeacons & geo fences. 176 | * [LocationManager](https://github.com/intuit/LocationManager) - Provides a block-based asynchronous API to request the current location, either once or continuously. 177 | * [LocationKit](https://locationkit.io) - Advanced location SDK - highly accurate location data with very low battery drain and contextual location information 178 | 179 | ##### iBeacon 180 | * [Proxitee](https://github.com/Proxitee/iOS-SDK) - Allows developers to create proximity aware applications utilizing iBeacons & geo fences. 181 | * [OWUProximityManager](https://github.com/ohwutup/OWUProximityManager) - A handy iBeacon + CoreBluetooth manager. 182 | * [Vicinity](https://github.com/Instrument/Vicinity) - Vicinity replicates iBeacons (by analyzing RSSI) and supports broadcasting and detecting low-energy bluetooth devices in the background. 183 | * [BeaconEmitter](https://github.com/lgaches/BeaconEmitter) - Turn your Mac as an iBeacon. 184 | * [OWUProximityManager](https://github.com/ohwutup/OWUProximityManager) - iBeacons + CoreBluetooth. 185 | 186 | ### HUD 187 | * [MBProgressHUD](https://github.com/jdg/MBProgressHUD) - Drop-in class for displays a translucent HUD with an indicator and/or labels while work is being done in a background thread. 188 | * [SVProgressHUD](https://github.com/TransitApp/SVProgressHUD) - A clean and lightweight progress HUD for your iOS app. 189 | * [ProgressHUD](https://github.com/relatedcode/ProgressHUD) - ProgressHUD is a lightweight and easy-to-use HUD. 190 | * [M13ProgressSuite](https://github.com/Marxon13/M13ProgressSuite) - A suite containing many tools to display progress information on iOS. 191 | * [JHProgressHUD](https://github.com/harikrishnant1991/JHProgressHUD) - An easy and lightweight Swift library to show HUD in IOS applications. :large_orange_diamond: 192 | * [PKHUD](https://github.com/pkluz/PKHUD) - A Swift based reimplementation of the Apple HUD (Volume, Ringer, Rotation,…) for iOS 8 and above. :large_orange_diamond: 193 | * [CozyLoadingActivity](https://github.com/goktugyil/CozyLoadingActivity) - Lightweight loading activity HUD. :large_orange_diamond: 194 | 195 | ### EventBus 196 | * [Caravel](https://github.com/coshx/caravel) - A Swift event bus for UIWebView and JS :large_orange_diamond: 197 | * [SwiftEventBus](https://github.com/cesarferreira/SwiftEventBus) - A publish/subscribe event bus optimized for iOS8. :large_orange_diamond: 198 | * [PromiseKit](https://github.com/mxcl/PromiseKit) - Promises for iOS and OS X. 199 | * [Bolts](https://github.com/BoltsFramework/Bolts-iOS) - Bolts is a collection of low-level libraries designed to make developing mobile apps easier, including tasks (promises) and app links (deep links). 200 | * [SwiftTask](https://github.com/ReactKit/SwiftTask) - Promise + progress + pause + cancel + retry for Swift. :large_orange_diamond: 201 | 202 | ### Files 203 | * [FileKit](https://github.com/nvzqz/FileKit) - Simple and expressive file management in Swift. :large_orange_diamond: 204 | 205 | ### JSON 206 | * [JSONKit](https://github.com/johnezang/JSONKit) - Objective-C JSON. 207 | * [TouchJSON](https://github.com/TouchCode/TouchJSON) - A humane JSON Objective-C un-framework. 208 | * [JSON-Framework](https://github.com/stig/json-framework) - This framework implements a strict JSON parser and generator in Objective-C. 209 | * [Mantle](https://github.com/Mantle/Mantle) - Model framework for Cocoa and Cocoa Touch. 210 | * [Groot](https://github.com/gonzalezreal/Groot) - Convert JSON dictionaries and arrays to and from Core Data managed objects. 211 | * [KZPropertyMapper](https://github.com/krzysztofzablocki/KZPropertyMapper) - Data mapping and validation with minimal amount of code. 212 | * [JSONModel](https://github.com/icanzilb/JSONModel) - Magical Data Modelling Framework for JSON. Create rapidly powerful, atomic and smart data model classes. 213 | * [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) - The better way to deal with JSON data in Swift. :large_orange_diamond: 214 | * [FastEasyMapping](https://github.com/Yalantis/FastEasyMapping) - Serialize & deserialize JSON fast. 215 | * [OCMapper](https://github.com/aryaxt/OCMapper) - Objective-C & Swift library to easily map NSDictionary to model objects. :large_orange_diamond: 216 | * [ObjectMapper](https://github.com/Hearst-DD/ObjectMapper) - A framework written in Swift that makes it easy for you to convert your Model objects (Classes and Structs) to and from JSON. :large_orange_diamond: 217 | * [JASON](https://github.com/delba/JASON) - JSON parsing with outstanding performances and convenient operators. :large_orange_diamond: 218 | * [Gloss](https://github.com/hkellaway/Gloss) - A shiny JSON parsing library in Swift. :large_orange_diamond: 219 | * [Cereal](https://github.com/Weebly/Cereal) - Swift object serialization :large_orange_diamond: 220 | 221 | ### Layout 222 | * [ios-flexboxkit](https://github.com/alexdrone/ios-flexboxkit) - A simple UIKit extension to wrap Flexbox layouts. 223 | * [Masonry](https://github.com/SnapKit/Masonry) - Harness the power of AutoLayout NSLayoutConstraints with a simplified, chainable and expressive syntax. 224 | * [FLKAutoLayout](https://github.com/floriankugler/FLKAutoLayout) - UIView category which makes it easy to create layout constraints in code. 225 | * [Façade](https://github.com/mamaral/Facade) - Programmatic view layout for the rest of us - an autolayout alternative. 226 | * [PureLayout](https://github.com/PureLayout/PureLayout) - The ultimate API for iOS & OS X Auto Layout — impressively simple, immensely powerful. Objective-C and Swift compatible. 227 | * [SnapKit](https://github.com/SnapKit/SnapKit) - A Swift Autolayout DSL for iOS & OS X. :large_orange_diamond: 228 | * [Cartography](https://github.com/robb/Cartography) - A declarative Auto Layout DSL for Swift :large_orange_diamond: 229 | 230 | ### Logging 231 | * [CleanroomLogger](https://github.com/emaloney/CleanroomLogger) - A configurable and extensible Swift-based logging API that is simple, lightweight and performant. :large_orange_diamond: 232 | * [CocoaLumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack) - A fast & simple, yet powerful & flexible logging framework for Mac and iOS. 233 | * [NSLogger](https://github.com/fpillet/NSLogger) - a high perfomance logging utility which displays traces emitted by client applications running on Mac OS X, iOS and Android. 234 | * [Aardvark](https://github.com/square/Aardvark/) - A performant logging framework that makes it dead simple to create actionable bug reports on iOS. 235 | * [BlockTypeDescription](https://github.com/conradev/BlockTypeDescription) - Show type signatures when logging blocks. 236 | * [QorumLogs](https://github.com/goktugyil/QorumLogs) — Swift Logging Utility for Xcode & Google Docs. :large_orange_diamond: 237 | 238 | ### Maps 239 | * [Route-me](https://github.com/route-me/route-me) - Open source map library for iOS. 240 | * [NAMapKit](https://github.com/neilang/NAMapKit) - Allows you to use custom maps in iphone applications and attempts to mimics some of the behaviour of the MapKit framework. 241 | * [Mapbox GL](https://github.com/mapbox/mapbox-gl-native) - An OpenGL renderer for Mapbox Vector Tiles with SDK bindings for iOS. 242 | * [CMMapLauncher](https://github.com/citymapper/CMMapLauncher) - iOS library that makes it quick and easy to show directions in various mapping applications. 243 | 244 | ### Media 245 | ##### Image 246 | * [GPU Image](https://github.com/BradLarson/GPUImage) - An open source iOS framework for GPU-based image and video processing. 247 | * [UIImage DSP](https://github.com/gdawg/uiimage-dsp) - IOS UIImage processing functions using the vDSP/Accelerate framework for speed. 248 | * [QR Code Scanner](http://www.appcoda.com/qr-code-ios-programming-tutorial/) - QR Code implementation. 249 | * [AsyncImageView](https://github.com/nicklockwood/AsyncImageView) - Simple extension of UIImageView for loading and displaying images asynchronously without lock up the UI. 250 | * [SDWebImage](https://github.com/rs/SDWebImage) - Asynchronous image downloader with cache support with an UIImageView category. 251 | * [DFImageManager](https://github.com/kean/DFImageManager) - Modern framework for fetching images from various sources. Zero config yet immense customization and extensibility. Uses NSURLSession. 252 | * [MapleBacon](https://github.com/zalando/MapleBacon) - An image download and caching library for iOS written in Swift. :large_orange_diamond: 253 | * [NYTPhotoViewer](https://github.com/NYTimes/NYTPhotoViewer) - Slideshow and image viewer. 254 | * [IDMPhotoBrowser](https://github.com/ideaismobile/IDMPhotoBrowser) - Photo Browser / Viewer. 255 | * [JTSImageViewController](https://github.com/jaredsinclair/JTSImageViewController) - Interactive iOS image viewer. 256 | * [Concorde](https://github.com/contentful-labs/Concorde/) - Download and decode progressive JPEGs. 257 | * [SCRecorder](https://github.com/rFlex/SCRecorder) - Camera engine with Vine-like tap to record, animatable filters, slow motion, segments editing. 258 | * [HanekeSwift](https://github.com/Haneke/HanekeSwift) - A lightweight generic cache for iOS written in Swift with extra love for images. :large_orange_diamond: 259 | * [TOCropViewController](https://github.com/TimOliver/TOCropViewController) - A view controller that allows users to crop UIImage objects. 260 | * [YXTMotionView](https://github.com/hanton/YXTMotionView) - A custom image view that implements device motion scrolling. 261 | * [PINRemoteImage](https://github.com/pinterest/PINRemoteImage) - A thread safe, performant, feature rich image fetcher. 262 | * [SABlurImageView](https://github.com/szk-atmosphere/SABlurImageView) - Easily Adding Animated Blur/Unblur Effects To An Image. :large_orange_diamond: 263 | * [FastImageCache](https://github.com/path/FastImageCache) - iOS library for quickly displaying images while scrolling. 264 | * [BKAsciiImage](https://github.com/bkoc/BKAsciiImage) - A library to render UIImage as ASCII art 265 | * [YLGIFImage](https://github.com/liyong03/YLGIFImage) - Async GIF image decoder and Image viewer supporting play GIF images. It just use very less memory. 266 | * [AlamofireImage](https://github.com/Alamofire/AlamofireImage) - An image component library for Alamofire. :large_orange_diamond: 267 | * [Nuke](https://github.com/kean/Nuke) - Advanced framework for managing images :large_orange_diamond: 268 | * [FlagKit](https://github.com/madebybowtie/FlagKit) - Beautiful flag icons for usage in apps and on the web. :large_orange_diamond: 269 | * [YYWebImage](https://github.com/ibireme/YYWebImage) - Asynchronous image loading framework (supports WebP, APNG, GIF). 270 | 271 | ##### Video 272 | * [VIMVideoPlayer](https://github.com/vimeo/VIMVideoPlayer) - A simple wrapper around the AVPlayer and AVPlayerLayer classes. 273 | * [MobilePlayer](https://github.com/mobileplayer/mobileplayer-ios) - A powerful and completely customizable media player for iOS. 274 | 275 | ##### PDF 276 | * [Reader](https://github.com/vfr/Reader) - PDF Reader Core for iOS. 277 | * [UIView 2 PDF](https://github.com/RobertAPhillips/UIView_2_PDF) - https://github.com/RobertAPhillips/UIView_2_PDF 278 | 279 | ### Messaging 280 | * [LayerKit](https://github.com/layerhq/releases-ios) - iOS SDK for Layer, the easiest way to add in-app messaging (text, photos, videos, data) to any mobile or web application. 281 | * [Twilio](https://www.twilio.com/) - Power modern communications. Build the next generation of voice and SMS applications. 282 | * [Plivo](https://www.plivo.com/) - SMS API, Voice API, & Global Carrier Provider. 283 | * [XMPPFramework](https://github.com/robbiehanson/XMPPFramework) - An XMPP Framework in Objective-C for Mac and iOS. 284 | 285 | ### Networking 286 | * [AFNetworking](https://github.com/AFNetworking/AFNetworking) - A delightful iOS and OS X networking framework. 287 | * [RestKit](https://github.com/RestKit/RestKit) - RestKit is an Objective-C framework for iOS that aims to make interacting with RESTful web services simple, fast and fun. 288 | * [FSNetworking](https://github.com/foursquare/FSNetworking) - Foursquare iOS networking library. 289 | * [ASIHTTPRequest](https://github.com/pokeb/asi-http-request) - Easy to use CFNetwork wrapper for HTTP requests, Objective-C, Mac OS X and iPhone. 290 | * [Overcoat](https://github.com/Overcoat/Overcoat) - Small but powerful library that makes creating REST clients simple and fun. 291 | * [ROADFramework](https://github.com/epam/road-ios-framework) - Attributed-oriented approach for interacting with web services. The framework has built-in json and xml serialization for requests and responses and can be easily extensible. 292 | * [MBNetworkMonitor](https://github.com/emaloney/MBToolbox/blob/master/Code/Network/MBNetworkMonitor.h) - A modern replacement for Apple's `Reachability` class that uses CoreTelephony to report more [information about the user's network connection](https://rawgit.com/emaloney/MBToolbox/master/Documentation/html/Classes/MBNetworkMonitor.html). 293 | * [MBNetworkIndicator](https://github.com/emaloney/MBToolbox/blob/master/Code/Network/MBNetworkIndicator.h) - Provides a simple way to [coordinate the device's network activity indicator across multiple. requests](https://rawgit.com/emaloney/MBToolbox/master/Documentation/html/Classes/MBNetworkIndicator.html). 294 | * [Alamofire](https://github.com/Alamofire/Alamofire) - Alamofire is an HTTP networking library written in Swift, from the creator of AFNetworking. :large_orange_diamond: 295 | * [Transporter](https://github.com/nghialv/Transporter) - A tiny library makes uploading and downloading easier. :large_orange_diamond: 296 | * [CDZPinger](https://github.com/cdzombak/CDZPinger) - Easy-to-use ICMP Ping. 297 | * [NSRails](https://github.com/dingbat/nsrails) - Map client-side objects/classes to remote rest api objects/orm 298 | * [NKMultipeer](https://github.com/nathankot/NKMultipeer) - A testable abstraction over multipeer connectivity. :large_orange_diamond: 299 | * [CocoaAsyncSocket](https://github.com/robbiehanson/CocoaAsyncSocket) - Asynchronous socket networking library for Mac and iOS. 300 | * [Siesta](https://bustoutsolutions.github.io/siesta/) - Elegant abstraction for RESTful resources that untangles stateful messes. An alternative to callback- and delegate-based networking. :large_orange_diamond: 301 | * [Reachability.swift](https://github.com/ashleymills/Reachability.swift) - Replacement for Apple's Reachability re-written in Swift with closures :large_orange_diamond: 302 | 303 | ### Push Notifications 304 | * [Orbiter](https://github.com/mattt/Orbiter) - Push Notification Registration for iOS. 305 | * [PEM](https://github.com/fastlane/PEM) - Automatically generate push notification profiles for your server. 306 | * [Parse Push](https://parse.com/products/push) - Complete free Push Notifications Backend with the possibility of creating segments, scheduling and even A/B Testing. 307 | * [Urban Airship](https://www.urbanairship.com/products/platform#push-messages) - Paid Push Notifications backend. 308 | * [Growth Push](https://growthpush.com) - Paid Push Notifications. It is one of the most popular tool in Japan. 309 | * [APNS-Pusher](https://github.com/KnuffApp/APNS-Pusher) - The debug application for Apple Push Notification Service (APNS). 310 | 311 | ### Passbook 312 | * [passbook](https://github.com/frozon/passbook) - Passbook gem let's you create pkpass for passbook iOS 6+. 313 | * [Dubai](https://github.com/nomad/dubai) - Generate and Preview Passbook Passes. 314 | * [Passkit](https://passkit.com) - Design, Create and validate Passbook Passes. 315 | 316 | ### Permissions 317 | * [PermissionScope](https://github.com/nickoneill/PermissionScope) - Intelligent iOS permissions UI and unified API (Supports Location, Notifications, Camera, Contacts, Calendar, Photos, Microphone, BT, Activity Monitoring, HealthKit and CloudKit). :large_orange_diamond: 318 | * [Proposer](https://github.com/nixzhu/Proposer) - Make permission request easier (Supports Camera, Photos, Microphone, Contacts, Location). :large_orange_diamond: 319 | * [ICanHas](https://github.com/wircho/ICanHas) - Simplifies iOS user permission requests (Supports location, push notifications, camera, contacts, calendar, photos). :large_orange_diamond: 320 | * [VWWPermissionKit](https://github.com/zakkhoyt/VWWPermissionKit) - A visual permission manager for iOS. 321 | * [ISHPermissionKit](https://github.com/iosphere/ISHPermissionKit) - A unified way for iOS apps to request user permissions. 322 | * [JLPermissions](https://github.com/jlaws/JLPermissions) - An iOS pre-permissions utility that lets developers ask users on their own dialog for calendar, contacts, location, photos, reminders, twitter, push notifications and more, before making the system-based permission request. 323 | 324 | ### Text 325 | * [Twitter Text Obj](https://github.com/twitter/twitter-text) - An Objective-C implementation of Twitter's text processing library. 326 | * [Nimbus](http://nimbuskit.info/) - Nimbus is a toolkit for experienced iOS software designers. 327 | * [NSStringEmojize](https://github.com/diy/nsstringemojize) - A category on NSString to convert Emoji Cheat Sheet codes to their equivalent Unicode characters. 328 | * [MMMarkdown](https://github.com/mdiep/MMMarkdown) - An Objective-C static library for converting Markdown to HTML. 329 | * [DTCoreText](https://github.com/Cocoanetics/DTCoreText) - Methods to allow using HTML code with CoreText. 330 | * [DTRichTextEditor](https://github.com/Cocoanetics/DTRichTextEditor) - A rich-text editor for iOS. 331 | * [NBEmojiSearchView](https://github.com/neerajbaid/NBEmojiSearchView) - A searchable emoji dropdown view that can be integrated with a text control 332 | * [ios-fontawesome](https://github.com/alexdrone/ios-fontawesome) - NSString+FontAwesome. 333 | * [Pluralize.swift](https://github.com/joshualat/Pluralize.swift) - Great Swift String Pluralize Extension :large_orange_diamond: 334 | * [RichEditorView](https://github.com/cjwirth/RichEditorView) - RichEditorView is a simple, modular, drop-in UIView subclass for Rich Text Editing. :large_orange_diamond: 335 | 336 | ### Walkthrough / Intro / Tutorial 337 | * [Onboard](https://github.com/mamaral/Onboard) - Easily create a beautiful and engaging onboarding experience with only a few lines of code. 338 | * [EAIntroView](https://github.com/ealeksandrov/EAIntroView) - Highly customizable drop-in solution for introduction views. 339 | * [MYBlurIntroductionView](https://github.com/MatthewYork/MYBlurIntroductionView) - A super-charged version of MYIntroductionView for building custom app introductions and tutorials. 340 | * [BWWalkthrough](https://github.com/ariok/BWWalkthrough) - A class to build custom walkthroughs for your iOS App. 341 | * [GHWalkThrough](https://github.com/GnosisHub/GHWalkThrough) - A UICollectionView backed drop-in component for introduction views. 342 | * [ICETutorial](https://github.com/icepat/ICETutorial) - A nice tutorial like the one introduced in the Path 3.X App. 343 | * [JazzHands](https://github.com/IFTTT/JazzHands) - Jazz Hands is a simple keyframe-based animation framework for UIKit. Animations can be controlled via gestures, scroll views, KVO, or ReactiveCocoa. 344 | * [RazzleDazzle](https://github.com/IFTTT/RazzleDazzle) - A simple keyframe-based animation framework for iOS, written in Swift. Perfect for scrolling app intros. :large_orange_diamond: 345 | * [Instructions](https://github.com/ephread/Instructions) - Easily add customizable coach marks into you iOS project. :large_orange_diamond: 346 | * [SwiftyWalkthrough](https://github.com/ruipfcosta/SwiftyWalkthrough) - The easiest way to create a great walkthrough experience in your apps, powered by Swift. :large_orange_diamond: 347 | * [Primer](https://www.goprimer.com) - Easy SDK for creating personalized landing screens, signup, and login flows on a visual editor with built in a/b/n testing and analytics. 348 | 349 | ### URL Scheme 350 | * [WAAppRouting](https://github.com/Wasappli/WAAppRouting) - iOS routing done right. Handles both URL recognition and controller displaying with parsed parameters. All in one line, controller stack preserved automatically! 351 | * [DeepLinkKit](https://github.com/usebutton/DeepLinkKit) - A splendid route-matching, block-based way to handle your deep links. 352 | * [IntentKit](https://github.com/intentkit/IntentKit) - An easier way to handle third-party URL schemes in iOS apps. 353 | * [JLRoutes](https://github.com/joeldev/JLRoutes) - URL routing library for iOS with a simple block-based API. 354 | 355 | ### UI 356 | * [Chameleon](https://github.com/ViccAlexander/Chameleon) - A lightweight, yet powerful, flat color framework for iOS (ObjC & Swift). :large_orange_diamond: 357 | * [ActionSheetPicker-3.0](https://github.com/skywinder/ActionSheetPicker-3.0/) - Quickly reproduce the dropdown UIPickerView / ActionSheet functionality on iOS. 358 | * [FlatUIKit](https://github.com/Grouper/FlatUIKit) - A collection of awesome flat UI components for iOS. 359 | * [JVFloatLabeledTextField](https://github.com/jverdi/JVFloatLabeledTextField) - UITextField subclass with floating labels. 360 | * [SSBouncyButton](https://github.com/StyleShare/SSBouncyButton) - iOS7-style bouncy button UI component. 361 | * [BetweenKit](https://github.com/ice3-software/between-kit) - A robust drag-and-drop framework for iOS. 362 | * [JSQMessagesViewController](https://github.com/jessesquires/JSQMessagesViewController) - An elegant messages UI library for iOS. 363 | * [AMSmoothAlert](https://github.com/mtonio91/AMSmoothAlert) - A cool AlertView. 364 | * [TSMessages](https://github.com/KrauseFx/TSMessages) - Show notification views on top of screen such as success, error, warning or messages for iOS. 365 | * [NZAlertView](https://github.com/NZN/NZAlertView) - Simple and intuitive alert view. Similar to push notification effect. 366 | * [MGSwipeTableCell](https://github.com/MortimerGoro/MGSwipeTableCell) - UITableViewCell subclass that allows to display swippable buttons with a variety of transitions. 367 | * [ARAutocompleteTextView](https://github.com/alexruperez/ARAutocompleteTextView) - subclass of UITextView that automatically displays text suggestions in real-time. Perfect for email Textviews. 368 | * [TGCameraViewController](https://github.com/tdginternet/TGCameraViewController) - Custom camera with AVFoundation. Beautiful, light and easy to integrate with iOS projects. 369 | * [ENSwiftSideMenu](https://github.com/evnaz/ENSwiftSideMenu) - A simple side menu for iOS 7/8 written in Swift. :large_orange_diamond: 370 | * [MDCSwipeToChoose](https://github.com/modocache/MDCSwipeToChoose) - Swipe to "like" or "dislike" any view, just like Tinder.app. Build a flashcard app, a photo viewer, and more, in minutes, not hours! 371 | * [ParallaxTableViewHeader](https://github.com/Vinodh-G/ParallaxTableViewHeader) - Parallax scrolling effect on UITableView header view when a tableView is scrolled. 372 | * [JLToast](https://github.com/devxoul/JLToast) - Toast for iOS with very simple interface. :large_orange_diamond: 373 | * [SweetAlert](https://github.com/codestergit/SweetAlert-iOS) - Live animated Alert View for iOS written in Swift. :large_orange_diamond: 374 | * [Form](https://github.com/hyperoslo/Form) - JSON driven form 375 | * [BLKFlexibleHeightBar](https://github.com/bryankeller/BLKFlexibleHeightBar) - Create condensing header bars like those seen in the Facebook, Square Cash, and Safari iOS apps. 376 | * [NMPopUpView](https://github.com/psy2k/NMPopUpView) - Simple iOS class for showing nice popup windows. Swift and Objective-C versions available. :large_orange_diamond: 377 | * [SDevIconFonts](https://github.com/0x73/SDevIconFonts) - Fontawesome, Iconic, Ionicons, Octicon porting for swift. :large_orange_diamond: 378 | * [SDevBootstrapButton](https://github.com/0x73/SDevBootstrapButton) - Twitter Bootstrap buttons for Swift :large_orange_diamond: 379 | * [SDevCircleButton](https://github.com/0x73/SDevCircleButton) - Circle Button for Swift :large_orange_diamond: 380 | * [SDevFlatColors](https://github.com/0x73/SDevFlatColors) - Flat Colors on Swift :large_orange_diamond: 381 | * [ColorArt](https://github.com/vinhnx/ColorArt) - extract dominant colors from image like iTunes 11. 382 | * [IQKeyboardManager](https://github.com/hackiftekhar/IQKeyboardManager) - Codeless drop-in universal library allows to prevent issues of keyboard sliding up and cover UITextField/UITextView. 383 | * [WCFastCell](https://github.com/wczekalski/WCFastCell) - Fast cells for Tables/Collections scrolling (without animations). 384 | * [Motif](https://github.com/erichoracek/Motif) - A lightweight and customizable JSON stylesheet framework for iOS. 385 | * [VBFPopFlatButton](https://github.com/victorBaro/VBFPopFlatButton) - Flat button with 9 different states animated using Facebook POP. 386 | * [HTPressableButton](https://github.com/herinkc/HTPressableButton) - Flat design pressable button. 387 | * [Cool-iOS-Camera](https://github.com/GabrielAlva/Cool-iOS-Camera) - A fully customisable and modern camera implementation for iOS made with AVFoundation. 388 | * [AsyncDisplayKit](https://github.com/facebook/AsyncDisplayKit/) - AsyncDisplayKit is an iOS framework that keeps even the most complex user interfaces smooth and responsive. 389 | * [AMTagListView](https://github.com/andreamazz/AMTagListView) - UIScrollView subclass that allows to add a list of highly customizable tags. 390 | * [MotionBlur](https://github.com/fastred/MotionBlur) - MotionBlur allows you to add motion blur effect to iOS animations. 391 | * [PBJVision](https://github.com/piemonte/PBJVision) - iOS camera engine, features touch-to-record video, slow motion video, and photo capture. 392 | * [DynamicColor](https://github.com/yannickl/DynamicColor) - Yet another extension to manipulate colors easily in Swift. :large_orange_diamond: 393 | * [GaugeKit](https://github.com/skywinder/GaugeKit) - Customizable gauges. Easy reproduce Apple's style gauges. :large_orange_diamond: 394 | * [SVWebViewController](https://github.com/TransitApp/SVWebViewController) - A drop-in inline browser for your iOS app. 395 | * [SwiftWebVC](https://github.com/meismyles/SwiftWebVC) - A Swift implementation of SVWebViewController - a drop-in inline browser for your iOS app. :large_orange_diamond: 396 | * [MVAutocompletePlaceSearchTextField](https://github.com/mrugrajsinh/MVAutocompletePlaceSearchTextField) - A drop-in Autocompletion control for Place Search like Google Places, Uber, etc. 397 | * [MVMaterialView](https://github.com/mrugrajsinh/MVMaterialView) - Subclass of UIControls and UIButton to mimic Ripple effect of Material Design concept. 398 | * [Atlas](https://github.com/layerhq/Atlas-iOS) - A library of native iOS messaging user interface components for Layer. 399 | * [Swift-Prompts](https://github.com/GabrielAlva/Swift-Prompts) - A Swift library to design custom prompts with a great scope of options to choose from. :large_orange_diamond: 400 | * [IQDropDownTextField](https://github.com/hackiftekhar/IQDropDownTextField) - TextField with DropDown support using UIPickerView 401 | * [PJAlertView](https://github.com/PrajeetShrestha/PJAlertView) - Apple has deprecated beloved alert view but this library will add revamped alert view with far more customization possibility. 402 | * [CZPicker](https://github.com/chenzeyu/CZPicker) - A picker view shown as a popup for iOS. 403 | * [TisprCardStack](https://github.com/tispr/tispr-card-stack) - Library that allows to have cards UI. :large_orange_diamond: 404 | * [YXTPageView](https://github.com/hanton/YXTPageView) - A PageView, which supporting scrolling to transition between a UIView and a UITableView. 405 | * [DatePickerDialog](https://github.com/squimer/DatePickerDialog-iOS-Swift) - A Swift library that displays an UIDatePicker within an UIAlertView :large_orange_diamond: 406 | * [gifu](https://github.com/kaishin/gifu) - Highly performant animated GIF support for iOS in Swift :large_orange_diamond: 407 | * [SAHistoryNavigationViewController](https://github.com/szk-atmosphere/SAHistoryNavigationViewController) - SAHistoryNavigationViewController realizes iOS task manager like UI in UINavigationContoller,3D Touch Compatible. :large_orange_diamond: 408 | * [DOFavoriteButton](https://github.com/okmr-d/DOFavoriteButton) - Cute Animated Button written in Swift. :large_orange_diamond: 409 | * [LNRSimpleNotifications](https://github.com/LISNR/LNRSimpleNotifications) - Simple Swift in-app notifications. LNRSimpleNotifications is a simplified Swift port of [TSMessages](https://github.com/KrauseFx/TSMessages). :large_orange_diamond: 410 | * [NgKeyboardTracker](https://github.com/meiwin/NgKeyboardTracker) - Objective-C library for tracking keyboard in iOS apps. 411 | * [SAInboxViewController](https://github.com/szk-atmosphere/SAInboxViewController) - UIViewController subclass inspired by "Inbox by google" animated transitioning. :large_orange_diamond: 412 | * [TLYShyNavBar](https://github.com/telly/TLYShyNavBar) - Unlike all those arrogant UINavigationBar, this one is shy and humble! Easily create auto-scrolling navigation bars! 413 | * [BRYXBanner](https://github.com/bryx-inc/BRYXBanner) - A lightweight dropdown notification for iOS 7+, in Swift. :large_orange_diamond: 414 | * [NYAlertViewController](https://github.com/nealyoung/NYAlertViewController) - Highly configurable iOS Alert Views with custom content views. 415 | * [HDNotificationView](https://github.com/nhdang103/HDNotificationView) - Emulates the native Notification Banner UI for any alert. 416 | * [MZFormSheetPresentationController](https://github.com/m1entus/MZFormSheetPresentationController) - MZFormSheetPresentationController provides an alternative to the native iOS UIModalPresentationFormSheet, adding support for iPhone and additional opportunities to setup controller size and feel form sheet. 417 | * [AnimatedTransitionGallery](https://github.com/shu223/AnimatedTransitionGallery) - Collection of iOS 7 custom animated transitions using UIViewControllerAnimatedTransitioning protocol. 418 | * [iCarousel](https://github.com/nicklockwood/iCarousel) - A simple, highly customisable, data-driven 3D carousel for iOS and Mac OS. 419 | * [RESideMenu](https://github.com/romaonthego/RESideMenu) - iOS 7/8 style side menu with parallax effect inspired by Dribbble shots. 420 | * [FontAwesomeKit](https://github.com/PrideChung/FontAwesomeKit) - Icon font library for iOS. Currently supports Font-Awesome, Foundation icons, Zocial, and ionicons. 421 | * [Cocoa Controls](https://www.cocoacontrols.com/) - Open source UI components for iOS and OS X. 422 | * [ActiveLabel.swift](https://github.com/optonaut/ActiveLabel.swift) - UILabel drop-in replacement supporting Hashtags (#), Mentions (@) and URLs (http://) :large_orange_diamond: 423 | * [XLForm](https://github.com/xmartlabs/XLForm) - XLForm is the most flexible and powerful iOS library to create dynamic table-view forms. Fully compatible with Swift & Obj-C. 424 | * [RAReorderableLayout](https://github.com/ra1028/RAReorderableLayout) - A UICollectionView layout whitch can move item with drag and drop. 425 | * [ESTabBarController](https://github.com/ezescaruli/ESTabBarController) - A tab bar controller for iOS that allows highlighting buttons and setting custom actions to them. 426 | * [STPopup](https://github.com/kevin0571/STPopup) - STPopup provides a UINavigationController in popup style, for both iPhone and iPad. 427 | * [HoneycombView](https://github.com/suzuki-0000/HoneycombView) - HoneycombView is the iOS UIView for displaying like Honyecomb layout written by swift. :large_orange_diamond: 428 | * [tapkulibrary](https://github.com/devinross/tapkulibrary) - tap + haiku = tapku, a well crafted open source iOS framework. 429 | * [NVActivityIndicatorView](https://github.com/ninjaprox/NVActivityIndicatorView) - Collection of nice loading animations. :large_orange_diamond: 430 | * [KCJogDial](https://github.com/kciter/KCJogDial) - Controllable UIView like jog dial. :large_orange_diamond: 431 | * [PagingMenuController](https://github.com/kitasuke/PagingMenuController) - Paging view controller with customizable menu in Swift. :large_orange_diamond: 432 | * [RadialMenu](https://github.com/bradjasper/radialmenu) - RadialMenu is a custom control for providing a touch context menu (like iMessage recording in iOS 8) built with Swift & POP :large_orange_diamond: 433 | * [VLDContextSheet](https://github.com/vangelov/VLDContextSheet) - Context menu similar to the one in the Pinterest iOS app 434 | * [cariocamenu](https://github.com/arn00s/cariocamenu) - The fastest zero-tap iOS menu. :large_orange_diamond: 435 | * [DAExpandAnimation](https://github.com/ifitdoesntwork/DAExpandAnimation) - A custom modal transition that presents a controller with an expanding effect while sliding out the presenter remnants. :large_orange_diamond: 436 | * [ScrollPager](https://github.com/aryaxt/ScrollPager) - A scroll pager similar to the one in Flipboard :large_orange_diamond: 437 | * [ComponentKit](http://componentkit.org/) - A React-Inspired View Framework for iOS, by Facebook. 438 | * [Eureka](https://github.com/xmartlabs/Eureka) - Elegant iOS form builder in pure Swift. :large_orange_diamond: 439 | * [PMTween](https://github.com/poetmountain/PMTween) - An elegant and flexible tweening library for iOS. 440 | * [MZTimerLabel](https://github.com/mineschan/MZTimerLabel) - A handy class for iOS to use UILabel as a countdown timer or stopwatch just like in Apple Clock App. 441 | * [WobbleView](https://github.com/inFullMobile/WobbleView) - WobbleView is an implementation of a recently popular wobble effect for any view in your app. It can be used to easily add dynamics to user interactions and transitions. :large_orange_diamond: 442 | * [CBZSplashView](https://github.com/callumboddy/CBZSplashView) - Twitter style Splash Screen View. Grows to reveal the Initial view behind. 443 | * [RKNotificationHub](https://github.com/cwRichardKim/RKNotificationHub) - Make any UIView a full fledged notification center. 444 | * [EatFit](https://github.com/Yalantis/EatFit) - Eat fit is a component for attractive data representation inspired by Google Fit 445 | * [RRNCollapsableSectionTableView](https://github.com/rob-nash/RRNCollapsableSectionTableView) -Collapsable table view sections with custom section header views. :large_orange_diamond: 446 | * [LiquidFloatingActionButton](https://github.com/yoavlt/LiquidFloatingActionButton) - Material Design Floating Action Button in liquid state 447 | * [LiquidLoader](https://github.com/yoavlt/LiquidLoader) - Spinner loader components with liquid animation :large_orange_diamond: 448 | * [PickerView](https://github.com/filipealva/PickerView) - A customizable alternative to UIPickerView in Swift. :large_orange_diamond: 449 | * [InteractivePlayerView](https://github.com/AhmettKeskin/InteractivePlayerView) - Custom iOS music player view :large_orange_diamond: 450 | * [phone-number-picker](https://github.com/hughbe/phone-number-picker) - A simple and easy to use view controller enabling you to enter a phone number with a country code similar to WhatsApp written in Swift :large_orange_diamond: 451 | * [DLWBouncyView](https://github.com/cute/DLWBouncyView) - BouncyView is an implementation of a recently popular bouncy effect for any view. 452 | * [MMPopupView](https://github.com/adad184/MMPopupView) - Pop-up based view(e.g. alert sheet), can easily customize. 453 | * [EXTView](https://github.com/recruit-mtl/EXTView) - Extended UIView for Interface Builder by using IB_DESIGNABLE and IBInspectable. 454 | * [JTMaterialSwitch](https://github.com/JunichiT/JTMaterialSwitch) - A customizable switch UI with ripple effect and bounce animations, inspired from Google's Material Design. 455 | * [PickerView](https://github.com/filipealva/PickerView) - A customizable alternative to UIPickerView written in Swift. 456 | * [KCSelectionDialog](https://github.com/kciter/KCSelectionDialog) - Simple selection dialog. :large_orange_diamond: 457 | * [SFFocusViewLayout](https://github.com/fdzsergio/SFFocusViewLayout) - UICollectionViewLayout with focused content. 458 | * [UITextField-Shake](https://github.com/andreamazz/UITextField-Shake) - UITextField category that adds shake animation. [Also with Swift version](https://github.com/King-Wizard/UITextField-Shake-Swift) :large_orange_diamond: 459 | * [JTFadingInfoView](https://github.com/JunichiT/JTFadingInfoView) - An UIButton-based view with fade in/out animation features. 460 | * [KCFloatingActionButton](https://github.com/kciter/KCFloatingActionButton) - Simple Floating Action Button for iOS :large_orange_diamond: 461 | * [TTGSnackbar](https://github.com/zekunyan/TTGSnackbar) - Show simple message and action button on the bottom of the screen with multi kinds of animation. :large_orange_diamond: 462 | * [TTGEmojiRate](https://github.com/zekunyan/TTGEmojiRate) - An emoji-liked rating view for iOS, implemented in Swift. :large_orange_diamond: 463 | * [CardAnimation](https://github.com/seedante/CardAnimation) - Card flipping-style animation :large_orange_diamond: 464 | * [BEMCheckBox](https://github.com/Boris-Em/BEMCheckBox#sample-app) - Tasteful Checkbox for iOS. (Check box) 465 | * [CVCalendar](https://github.com/Mozharovsky/CVCalendar) - A custom visual calendar for iOS 8+ written in Swift (2.0). :large_orange_diamond: 466 | * [SCLAlertView-Swift](https://github.com/vikmeup/SCLAlertView-Swift) - Beautiful animated Alert View, written in Swift. :large_orange_diamond: 467 | * [Atlas-iOS](https://atlas.layer.com/ios) - Atlas is a library of native iOS communications user interface components for Layer. 468 | * [TKRubberIndicator](https://github.com/TBXark/TKRubberIndicator) - Rubber Indicator in Swift http://tbxark.github.io :large_orange_diamond: 469 | * [HorizontalProgress](https://github.com/AliThink/HorizontalProgress) - Simple horizontal progress bar with animation 470 | * [TKSwitcherCollection](https://github.com/TBXark/TKSwitcherCollection) - An animate switch collection http://tbxark.github.io :large_orange_diamond: 471 | * [JDSwiftAvatarProgress](https://github.com/JellyDevelopment/JDSwiftAvatarProgress) - Easy customizable avatar image asynchronously with progress bar animated :large_orange_diamond: 472 | * [iOS-CircleProgressView](https://github.com/CardinalNow/iOS-CircleProgressView) - This control will allow a user to use code instantiated or interface builder to create and render a circle progress view. :large_orange_diamond: 473 | * [Hamburger-Menu-Button](https://github.com/toannt/Hamburger-Menu-Button) - A hamburger menu button with full customization. :large_orange_diamond: 474 | * [DGElasticPullToRefresh](https://github.com/gontovnik/DGElasticPullToRefresh) - Elastic pull to refresh for iOS developed in Swift :large_orange_diamond: 475 | * [HTYTextField](https://github.com/hanton/HTYTextField) - A UITextField with bouncy placeholder. :large_orange_diamond: 476 | 477 | ### WebSocket 478 | * [Socket Rocket](https://github.com/square/SocketRocket) - A conforming Objective-C WebSocket client library. 479 | 480 | ### Code Quality 481 | * [KZBootstrap](https://github.com/krzysztofzablocki/KZBootstrap) - Set of scripts and annotations that generate extra compile time errors and warnings on bad code quality. 482 | * [KZAsserts](https://github.com/krzysztofzablocki/KZAsserts) - Set of custom assertions that automatically generate NSError's, allow for both Assertions in Debug and Error handling in Release builds, with beautiful DSL. 483 | * [PSPDFUIKitMainThreadGuard](https://gist.github.com/steipete/5664345) - Simple snippet generating assertions when UIKit is used on background threads. 484 | * [Flex](https://github.com/Flipboard/FLEX) - An in-app debugging and exploration tool for iOS. 485 | * [chisel](https://github.com/facebook/chisel) - Collection of LLDB commands to assist debugging iOS apps. 486 | * [OCLint](http://oclint.org/) - Static code analysis tool for improving quality and reducing defects. 487 | * [ocstyle](https://github.com/Cue/ocstyle) - Objective-C style checker. 488 | * [SwiftLint](https://github.com/realm/SwiftLint) - An experimental tool to enforce Swift style and conventions. :large_orange_diamond: 489 | * [spacecommander](https://github.com/square/spacecommander) - Commit fully-formatted Objective-C code as a team without even trying. 490 | * [DWURecyclingAlert](https://github.com/diwu/DWURecyclingAlert) - Optimizing UITableViewCell For Fast Scrolling. 491 | * [DCIntrospect](https://github.com/domesticcatsoftware/DCIntrospect) - Small library of visual debugging tools for iOS. 492 | * [Watchdog](https://github.com/wojteklukaszuk/Watchdog) - Class for logging excessive blocking on the main thread :large_orange_diamond: 493 | * [Tailor](http://tailor.sh/) - Cross-platform static analyzer for Swift that helps you to write cleaner code and avoid bugs. 494 | 495 | ### Analytics 496 | * [Flurry Analytics](http://www.flurry.com) - Free app Analytics API. 497 | * [Parse Analytics](https://parse.com/products/analytics) - Measure App Usage, track bugs and much more. 498 | * [Mixpanel](https://mixpanel.com/) - Advanced analytics platform. 499 | * [Localytics](http://www.localytics.com/) - Brings app marketing and analytics together. 500 | * [Answers by Fabric](https://answers.io/) - Answers gives you real-time insight into people’s experience in your app. 501 | * [Liquid Analytics](https://onliquid.com) - Identify behaviours through Analytics and react with real-time Personalization. 502 | * [GTrack](https://github.com/gemr/GTrack) - Lightweight Objective-C wrapper around the Google Analytics for iOS SDK with some extra goodies. 503 | * [ARAnalytics](https://github.com/orta/ARAnalytics) - Analytics abstraction library offering a sane API for tracking events and user data. 504 | * [Segment](https://github.com/segmentio/analytics-ios) - The hassle-free way to integrate analytics into any iOS application. 505 | 506 | ### Payments 507 | * [Stripe](https://stripe.com) - Payment integration on your app with PAY. Suitable for people with low knowlege on Backend. 508 | * [Braintree](https://www.braintreepayments.com) - Free payment processing on your first $50k. Requires Backend. 509 | * [Venmo](https://github.com/venmo/venmo-ios-sdk) Make and accept payments in your iOS app via Venmo. 510 | * [Moltin](https://moltin.com/ios-ecommerce-sdk) - Add eCommerce to your app with a simple SDK, so you can create a store and sell physical products, no backend required. 511 | 512 | ### Products 513 | * [Import.io](https://import.io) - Instantly Turn Web Pages into Data. 514 | * [Tapglue](https://www.tapglue.com) - Build social products and a activity feed with a few lines of code. 515 | 516 | ### Utility 517 | * [Underscore.m](https://github.com/robb/Underscore.m) - A DSL for Data Manipulation. 518 | * [SBConstants](https://github.com/paulsamuels/SBConstants) - Generate a constants file by grabbing identifiers from storyboards in a project. 519 | * [XExtensionItem](https://github.com/tumblr/XExtensionItem) - Easier sharing of structured data between iOS applications and share extensions. 520 | * [ReflectableEnum](https://github.com/fastred/ReflectableEnum) - Reflection for enumerations in Objective-C. 521 | * [VWWPermissionKit](https://github.com/zakkhoyt/VWWPermissionKit) - A visual permission manager for iOS. 522 | * [ClusterPrePermissions](https://github.com/clusterinc/ClusterPrePermissions) - Reusable pre-permissions utility that lets developers ask users for access in their own dialog, before making the system-based request. 523 | * [DateTools](https://github.com/MatthewYork/DateTools) - Dates and times made easy in Objective-C 524 | * [EKAlgorithms](https://github.com/EvgenyKarkan/EKAlgorithms) - Some well known CS algorithms & data structures in Objective-C. 525 | * [Tactile](https://github.com/delba/Tactile) - A safer and more idiomatic way to respond to gestures and control events. :large_orange_diamond: 526 | * [Colours](https://github.com/bennyguitar/Colours) - A beautiful set of predefined colors and a set of color methods to make your iOS/OSX development life easier. 527 | * [ObjectiveSugar](https://github.com/supermarin/ObjectiveSugar) - ObjectiveC additions for humans. Ruby style. 528 | * [GroundControl](https://github.com/mattt/GroundControl) - Remote configuration for iOS. 529 | * [OpinionatedC](https://github.com/leoschweizer/OpinionatedC) - Because Objective-C should have inherited more from Smalltalk. 530 | * [GCDKit](https://github.com/JohnEstropia/GCDKit) - Grand Central Dispatch simplified with Swift. :large_orange_diamond: 531 | * [SwiftRandom](https://github.com/thellimist/SwiftRandom) - Generator for random data. :large_orange_diamond: 532 | * [RandomKit](https://github.com/nvzqz/RandomKit/) - Random data generation in Swift. :large_orange_diamond: 533 | * [Async](https://github.com/duemunk/Async) - Syntactic sugar in Swift for asynchronous dispatches in Grand Central Dispatch :large_orange_diamond: 534 | 535 | ### Security 536 | * [UICKeyChainStore](https://github.com/kishikawakatsumi/UICKeyChainStore) - UICKeyChainStore is a simple wrapper for Keychain on iOS. 537 | * [cocoapods-keys](https://github.com/orta/cocoapods-keys) - A key value store for storing environment and application keys. 538 | * [Valet](https://github.com/square/Valet) - Securely store data in the iOS or OS X Keychain without knowing a thing about how the Keychain works. 539 | * [libextobjc](https://github.com/jspahrsummers/libextobjc) - A Cocoa library to extend the Objective-C programming language. 540 | * [Locksmith](https://github.com/matthewpalmer/Locksmith) - A powerful, protocol-oriented library for working with the keychain in Swift. :large_orange_diamond: 541 | 542 | # Project setup 543 | * [crafter](https://github.com/krzysztofzablocki/crafter) - CLI that allows you to configure iOS project's template using custom DSL syntax, simple to use and quite powerful. 544 | * [liftoff](https://github.com/thoughtbot/liftoff) - Another CLI for creating iOS projects. 545 | * [KZBootstrap](https://github.com/krzysztofzablocki/KZBootstrap) - iOS project bootstrap aimed at high quality coding. 546 | * [amaro](https://github.com/crushlovely/Amaro) - iOS Boilerplate full of delights. 547 | * [chairs](https://github.com/orta/chairs) - Swap around your iOS Simulator Documents 548 | 549 | # Dependency / Package Manager 550 | * [Cocoa Pods](https://cocoapods.org/) - CocoaPods is the dependency manager for Objective-C projects. It has thousands of libraries and can help you scale your projects elegantly. 551 | * [Xcode Maven](http://sap-production.github.io/xcode-maven-plugin/site/) - The Xcode Maven Plugin can be used in order to run Xcode builds embedded in a Maven lifecycle. 552 | * [Gradle](http://openbakery.org/gradle.html) - The gradle xcode plugin can be used to build iOS or Mac OS X Projects using gradle. 553 | * [Carthage](https://github.com/Carthage/Carthage) - A simple, decentralized dependency manager for Cocoa. :large_orange_diamond: 554 | * [SWM (Swift Modules)](https://github.com/jankuca/swm) - A package/dependency manager for Swift projects similar to npm (node.js package manager) or bower (browser package manager from Twitter). Does not require the use of XCode. :large_orange_diamond: 555 | * [Alcatraz](http://alcatraz.io/) - The package manager for Xcode. 556 | * [CocoaSeeds](https://github.com/devxoul/CocoaSeeds) - Git Submodule Alternative for Cocoa. 557 | 558 | 559 | # Testing 560 | 561 | ### TDD / BDD 562 | * [Kiwi](https://github.com/kiwi-bdd/Kiwi) - A behavior-driven development library for iOS development. 563 | * [Specta](https://github.com/specta/specta) - A light-weight TDD / BDD framework for Objective-C & Cocoa. 564 | * [Quick](https://github.com/Quick/Quick) - A behavior-driven development framework for Swift and Objective-C. 565 | * [XcodeCoverage](https://github.com/jonreid/XcodeCoverage) - Code coverage for Xcode projects. 566 | * [OHHTTPStubs](https://github.com/AliSoftware/OHHTTPStubs) - Stub your network requests easily! Test your apps with fake network data and custom response time, response code and headers! 567 | * [Dixie](https://github.com/Skyscanner/Dixie) - Dixie is an open source Objective-C testing framework for altering object behaviours. 568 | * [gh-unit](https://github.com/gh-unit/gh-unit) - Test Framework for Objective-C. 569 | 570 | ### UI Testing 571 | * [CrashMonkey](https://github.com/mokemokechicken/CrashMonkey) - Monkey Test Tool For iOS. 572 | * [appium](http://appium.io/) - Appium is an open source test automation framework for use with native and hybrid mobile apps. 573 | * [robotframework-appiumlibrary](https://github.com/jollychang/robotframework-appiumlibrary) - AppiumLibrary is an appium testing library for RobotFramework. 574 | * [Cucumber](https://cucumber.io/) - Behavior driver development for iOS. 575 | * [Kif](https://github.com/kif-framework/KIF) - An iOS Functional Testing Framework. 576 | * [Subliminal](https://github.com/inkling/Subliminal) - An understated approach to iOS integration testing. 577 | * [UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/) - JavaScript library to write test scripts that exercise your app’s user interface elements while the app runs on a connected device. 578 | * [ios-driver](http://ios-driver.github.io/ios-driver/index.html) - Test any IOS native, hybrid, or mobile web application using Selenium / WebDriver. 579 | * [Zucchini](https://github.com/zucchini-src/zucchini) - A visual iOS testing framework that loves your apps. 580 | * [Remote](https://github.com/johnno1962/Remote) - Control your iPhone from inside Xcode for end-to-end testing. 581 | 582 | ### Other Testing 583 | * [NaughtyKeyboard](https://github.com/Palleas/NaughtyKeyboard) - The Big List of Naughty Strings is a list of strings which have a high probability of causing issues when used as user-input data. This is a keyboard to help you test your app from your iOS device. 584 | * [PonyDebugger](https://github.com/square/PonyDebugger) - Remote network and data debugging for your native iOS app using Chrome Developer Tools 585 | * [ios-snapshot-test-case](https://github.com/facebook/ios-snapshot-test-case) - Snapshot view unit tests for iOS. 586 | 587 | ### Beta Distribution 588 | * [Crashlytics](https://try.crashlytics.com/) - A crash reporting and beta testing service. 589 | * [TestFlight Beta Testing](https://developer.apple.com/testflight/) - The beta testing service hosted on iTunes Connect (requires iOS 8 or later). 590 | * [HockeyApp](http://hockeyapp.net/) - With HockeyApp, you can distribute beta versions of your app, collect live crash reports, get feedback from users, and analyze test coverage. 591 | * [boarding](https://github.com/fastlane/boarding) - Instantly create a simple signup page for TestFlight beta testers. 592 | * [HockeyKit](https://github.com/bitstadium/HockeyKit) - A software update kit. 593 | 594 | # Toolchains 595 | * [RubyMotion](http://www.rubymotion.com/) - RubyMotion is a revolutionary toolchain that lets you quickly develop and test native iOS and OS X applications for iPhone, iPad and Mac, all using the Ruby language. 596 | 597 | # Tools 598 | * [Shark](https://github.com/kaandedeoglu/Shark) - Swift Script that transforms the .xcassets folder into a type safe enum. :large_orange_diamond: 599 | * [R.swift](https://github.com/mac-cain13/R.swift) - Tool to get strong typed, autocompleted resources like images, cells and segues in your Swift project. :large_orange_diamond: 600 | * [SwiftGen](https://github.com/AliSoftware/SwiftGen) - A collection of Swift tools to generate Swift code (enums for your assets, storyboards, Localizable.strings and UIColors). :large_orange_diamond: 601 | * [Localize-Swift](https://github.com/marmelroy/Localize-Swift) - Swift 2.0 friendly localization and i18n with in-app language switching :large_orange_diamond: 602 | * [Blade](https://github.com/jondot/blade) - Generate Xcode image catalogs for iOS / OSX app icons, universal images, and more. 603 | * [Retini](https://github.com/terwanerik/Retini) - A super simple retina (2x, 3x) image converter. 604 | * [Provisioning](https://github.com/chockenberry/Provisioning) - A Quick Look plug-in to preview .mobileprovision files. 605 | 606 | # Rapid Development 607 | * [KZPlayground](https://github.com/krzysztofzablocki/KZPlayground) - Playgrounds for Objective-C for extremely fast prototyping / learning. 608 | * [dyci](https://github.com/DyCI/dyci-main) - Code injection tool. 609 | * [injectionforxcode](https://github.com/johnno1962/injectionforxcode) - Code injection including Swift. 610 | * [MMBarricade](https://github.com/mutualmobile/MMBarricade) - Runtime configurable local server for iOS apps. 611 | * [NetworkObjects](https://github.com/colemancda/NetworkObjects) - Generate RESTful server from your Core Data Model. 612 | * [STV Framework](http://www.sensiblecocoa.com) - Native visual iOS development. 613 | 614 | # Deployment 615 | * [fastlane](https://github.com/fastlane/fastlane) Connect all iOS deployment tools into one streamlined workflow. 616 | * [deliver](https://github.com/fastlane/deliver) Deploy screenshots, app metadata and app updates to the App Store using just one command. 617 | * [snapshot](https://github.com/fastlane/snapshot) Automatically create screenshots in all languages on all devices. 618 | 619 | # App Store 620 | * [Average App Store Review Times](http://appreviewtimes.com) This site tracks the average App Store review times for both the iOS and the Mac App Store using data crowdsourced from iOS and Mac developers. 621 | * [Apple's Common App Rejections Styleguide](https://developer.apple.com/app-store/review/rejections/) Highlighted some of the most common issues that cause apps to get rejected. 622 | * [Free App Store Optimization Tool](https://www.mobileaction.co) Lets you track your App Store visibility in terms of keywords and competitors. 623 | 624 | # SDK 625 | 626 | ## Official 627 | 628 | * [Spotify](https://github.com/spotify/ios-sdk) Spotify iOS SDK. 629 | * [Facebook](https://github.com/facebook/facebook-ios-sdk) Facebook iOS SDK. 630 | * [Google Analytics](https://developers.google.com/analytics/devguides/collection/ios/v3/) Google Analytics SDK for iOS 631 | * [Paypal iOS SDK](https://github.com/paypal/PayPal-iOS-SDK) The PayPal Mobile SDKs enable native apps to easily accept PayPal and credit card payments. 632 | * [Pocket](https://github.com/Pocket/Pocket-ObjC-SDK) SDK for saving stuff to Pocket. 633 | * [Tumblr](https://github.com/tumblr/TMTumblrSDK) Library for easily integrating Tumblr data into your iOS or OS X application. 634 | * [Evernote](https://github.com/evernote/evernote-sdk-ios) Evernote SDK for iOS. 635 | * [Box](https://github.com/box/box-ios-sdk) iOS + OS X SDK for the Box API. 636 | * [OneDrive](https://github.com/OneDrive/onedrive-sdk-ios) Live SDK for iOS. 637 | * [Stripe](https://github.com/stripe/stripe-ios) Stripe bindings for iOS and OS X. 638 | * [Venmo](https://github.com/venmo/venmo-ios-sdk) Make and accept payments in your iOS app via Venmo. 639 | * [AWS](https://github.com/aws/aws-sdk-ios) Amazon Web Services Mobile SDK for iOS. 640 | * [Zendesk](https://github.com/zendesk/zendesk_sdk_ios) Zendesk Mobile SDK for iOS. 641 | * [Adobe Creative SDK](https://creativesdk.adobe.com/) Adobe creative tools and Creative Cloud SDK. 642 | * [Dropbox](https://www.dropbox.com/developers) SDKs for Drop-ins and Dropbox Core API. 643 | * [Fabric by Twitter](https://docs.fabric.io/ios/index.html) Fabric Twitter Kit for iOS. 644 | * [Liquid Analytics](https://github.com/lqd-io/liquid-sdk-ios) Identify behaviours through Analytics and react with real-time Personalization. 645 | * [ResearchKit](https://github.com/ResearchKit/ResearchKit) ResearchKit is an open source software framework that makes it easy to create apps for medical research or for other research projects. 646 | * [PacketZoom](https://packetzoom.com) PacketZoom SDK for iOS. 647 | 648 | ## Unofficial 649 | 650 | * [STTwitter](https://github.com/nst/STTwitter) A stable, mature and comprehensive Objective-C library for Twitter REST API 1.1 651 | * [FHSTwitterEngine](https://github.com/fhsjaagshs/FHSTwitterEngine) Twitter API for Cocoa developers. 652 | * [Giphy](https://github.com/heyalexchoi/Giphy-iOS) Giphy API client for iOS in Objective-C. 653 | * [UberKit](https://github.com/sachinkesiraju/UberKit) - A simple, easy-to-use Objective-C wrapper for the Uber API. 654 | * [InstagramKit](https://github.com/shyambhat/InstagramKit) - Instagram iOS SDK. 655 | * [DribbbleSDK](https://github.com/agilie/dribbble-ios-sdk) - Dribbble iOS SDK. 656 | * [objectiveflickr](https://github.com/lukhnos/objectiveflickr) - ObjectiveFlickr, a Flickr API framework for Objective-C. 657 | 658 | # Xcode 659 | 660 | ### Plugins 661 | * [FuzzyAutocompletePlugin](https://github.com/FuzzyAutocomplete/FuzzyAutocompletePlugin) - A Xcode 5+ plugin that adds more flexible autocompletion rather than just prefix-matching. 662 | * [SCXcodeMiniMap](https://github.com/stefanceriu/SCXcodeMiniMap) - SCXcodeMiniMap is a plugin that adds a source editor MiniMap to Xcode. 663 | * [Show in Github](https://github.com/larsxschneider/ShowInGitHub) - Xcode plugin to open the GitHub page of the commit of the currently selected line in the editor window. 664 | * [BBUFullIssueNavigator](https://github.com/neonichu/BBUFullIssueNavigator) - Xcode plugin for showing all issue content in the issue navigator. 665 | * [BBUDebuggerTuckAway](https://github.com/neonichu/BBUDebuggerTuckAway) - Xcode plugin for auto-hiding the debugger once you start typing in the source code editor. 666 | * [SCXcodeSwitchExpander](https://github.com/stefanceriu/SCXcodeSwitchExpander) - SCXcodeSwitchExpander is a small Xcode plugin that expands switch statements by inserting missing cases. 667 | * [VVDocumenter-Xcode](https://github.com/onevcat/VVDocumenter-Xcode) - Xcode plug-in which helps you write Javadoc style documents easier. 668 | * [XAlign](https://github.com/qfish/XAlign) - An amazing Xcode plugin to align regular code. It can align anything by using custom alignment patterns. 669 | * [Cocoapods Xcode Plugin](https://github.com/kattrali/cocoapods-xcode-plugin) - Dependency management helper for your CocoaPods, right in Xcode. 670 | * [KSImageNamed-Xcode](https://github.com/ksuther/KSImageNamed-Xcode) - Xcode plug-in that provides autocomplete for imageNamed: calls. 671 | * [ColorSense-for-Xcode](https://github.com/omz/ColorSense-for-Xcode) - Plugin for Xcode to make working with colors more visual. 672 | * [Backlight-for-XCode](https://github.com/limejelly/Backlight-for-XCode) - Highlights the current editing line in Xcode 673 | * [UIColor-Hex-Swift](https://github.com/yeahdongcn/UIColor-Hex-Swift) - Convenience method for creating autoreleased color using RGBA hex string. :large_orange_diamond: 674 | * [KPRunEverywhereXcodePlugin](https://github.com/kitschpatrol/KPRunEverywhereXcodePlugin) - An Xcode plugin to build and run an app across multiple iOS devices with one click. 675 | * [RevealPlugin](https://github.com/shjborage/Reveal-Plugin-for-Xcode) - Plugin for Xcode to integrate the Reveal App to your project automatic. 676 | * [RealmPlugin](https://realm.io/docs/objc/0.81.0/#xcode-plugin)- Xcode plugin to generate new Realm models. 677 | * [AdjustFontSize](https://github.com/zats/AdjustFontSize-Xcode-Plugin) - Instant font size adjustment with `⌘ +` / `⌘ -`. 678 | * [Lin](https://github.com/questbeat/Lin) - Xcode plugin that provides auto-completion for NSLocalizedString. 679 | * [Rephrase](https://www.rephrase.io) - Localise from Xcode. 680 | * [XCActionBar](https://github.com/pdcgomes/XCActionBar) - "Alfred for Xcode" plugin. 681 | * [QuickJump](https://github.com/wiruzx/QuickJump) - Quick code navigation for Xcode. 682 | * [CATweaker](https://github.com/keefo/CATweaker) - Plugin for creating beautiful CAMediaTimingFunction curve. 683 | * [XcodeWay](https://github.com/onmyway133/XcodeWay) - An Xcode plugin that makes navigating to many places easier (available via Alcatraz). 684 | * [GitDiff](https://github.com/johnno1962/GitDiff) - Highlights deltas against git repo in Xcode. 685 | * [MCLog](https://github.com/yuhua-chen/MCLog) - Xcode plugin for filtering the console area. 686 | * [XToDo](https://github.com/trawor/XToDo) - Dialog with list of all TODO, FIXME, ??? and !!! in the project. 687 | * [CopyIssue](https://github.com/hanton/CopyIssue-Xcode-Plugin) - Makes Copy Xcode Issue Description Easy. 688 | * [RTImageAssets](https://github.com/rickytan/RTImageAssets) - A Xcode plugin to automatically generate all the App icons needed. 689 | * [BBUncrustifyPlugin-Xcode](https://github.com/benoitsan/BBUncrustifyPlugin-Xcode) - Xcode plugin to format source code using ClangFormat or Uncrustify. 690 | * [Aviator](https://github.com/marksands/Aviator) - Xcode plugin that brings ⇧⌘T (source/test toggle) from AppCode over to Xcode. 691 | * [JumpMarks](https://github.com/merrickp/JumpMarks) - Navigate your code files with numbered bookmarks. 692 | * [XCSnippetr](https://github.com/dzenbot/XCSnippetr) - An Xcode Plugin to upload code snippets directly into Slack and Gist. 693 | * [Peckham](https://github.com/markohlebar/Peckham) - Add #import-s from anywhere in the code. 694 | * [MLAutoReplace](https://github.com/molon/MLAutoReplace) - Xcode plugin, Re-Intent, make you write code more quickly. 695 | * [Chameleon](https://github.com/ViccAlexander/Chameleon) - Flat Color Framework for iOS (Obj-C & Swift) :large_orange_diamond: 696 | * [AutoHighlightSymbol](https://github.com/chiahsien/AutoHighlightSymbol) - A Xcode plugin to add highlight to the instances of selected symbol. 697 | * [Reveal-In-GitHub](https://github.com/lzwjava/Reveal-In-Github) - Xcode plugin to let you jump to GitHub History, Blame, PRs, Issues, Notifications of any GitHub repo with one shortcut. 698 | 699 | ### Themes 700 | * [Dracula Theme](https://github.com/zenorocha/dracula-theme) - A dark theme for Xcode. 701 | * [Xcode themes list](https://github.com/hdoria/xcode-themes) - Color themes for Xcode. 702 | * [Solarized-Dark-for-Xcode](https://github.com/ArtSabintsev/Solarized-Dark-for-Xcode/) - Solarized Dark Theme for Xcode 5. 703 | 704 | ### Other Xcode 705 | * [Synx](https://github.com/venmo/synx) - A command-line tool that reorganizes your Xcode project folder to match your Xcode groups. 706 | * [dsnip](https://github.com/Tintenklecks/IBDelegateCodesippets) - Tool to generate (native) Xcode code snippets from all protocols/delegate methods of UIKit (UITableView, ...) 707 | 708 | 709 | # Style Guides 710 | * [NY Times - Objective C Style Guide](https://github.com/NYTimes/objective-c-style-guide) - The Objective-C Style Guide used by The New York Times. 711 | * [raywenderlich Style Guide](https://github.com/raywenderlich/objective-c-style-guide) - A style guide that outlines the coding conventions for raywenderlich.com. 712 | * [Github Objective-C Style Guide](https://github.com/github/objective-c-style-guide) - Style guide & coding conventions for Objective-C projects. 713 | * [Objective-C Coding Convention and Best Practices](https://gist.github.com/soffes/812796) - Gist with coding conventions. 714 | * [Swift Style Guide by @raywenderlich](https://github.com/raywenderlich/swift-style-guide) - The official Swift style guide for raywenderlich.com. :large_orange_diamond: 715 | * [Spotify Objective-C Coding Style](https://github.com/spotify/ios-style) - Guidelines for iOS development in use at Spotify. 716 | * [Dropbox Objective-C Style Guide](https://dl.dropboxusercontent.com/s/5utnlwhr18ax05c/style-guide.html?dl=0) - 717 | * [Github - Style guide & coding conventions for Swift projects](https://github.com/github/swift-style-guide) - A guide to our Swift style and conventions by @github. :large_orange_diamond: 718 | * [Futurice iOS Good Practices](https://github.com/futurice/ios-good-practices) - iOS starting guide and good practices suggestions by [@futurice](https://github.com/futurice). 719 | 720 | # Good Websites 721 | 722 | ### News, Blogs and Feeds 723 | * [BGR](http://bgr.com/ios-7/) 724 | * [iMore](http://www.imore.com/) 725 | * [Lifehacker](http://lifehacker.com/tag/ios) 726 | * [iCode Blog](http://www.icodeblog.com/) 727 | * [NSHipster](http://nshipster.com) 728 | * [Objc.io](https://www.objc.io/) 729 | * [ASCIIwwdc](http://asciiwwdc.com) 730 | * [Natasha The Robot](http://natashatherobot.com) 731 | * [Apple's Swift Blog](https://developer.apple.com/swift/blog/) :large_orange_diamond: 732 | * [iOS Programming Subreddit](https://www.reddit.com/r/iosprogramming) 733 | * [iOS Dev Weekly](https://iosdevweekly.com/) 734 | * [iOS8-day-by-day](https://github.com/shinobicontrols/iOS8-day-by-day) :large_orange_diamond: 735 | * [iOScreator](http://www.ioscreator.com/) :large_orange_diamond: 736 | * [Mathew Sanders](http://mathewsanders.com/) :large_orange_diamond: 737 | * [Little Bites of Cocoa](https://littlebitesofcocoa.com/) :large_orange_diamond: 738 | * [iOS Dev Nuggets](http://hboon.com/iosdevnuggets/) :large_orange_diamond: 739 | * [This Week in Swift](http://swiftnews.curated.co) :large_orange_diamond: 740 | * [iOS Goodies](http://ios-goodies.com) 741 | 742 | ### UIKit references 743 | * [iOS Fonts](http://iosfonts.com/) 744 | * [UIAppearance list](https://gist.github.com/mattt/5135521) 745 | 746 | ### Forums and discuss lists 747 | * [iPhone Dev SDK Forum](http://iphonedevsdk.com/) 748 | * ["iOS" on Stackoverflow](http://stackoverflow.com/questions/tagged/ios) 749 | 750 | ### Tutorials and Keynotes 751 | * [AppCoda](http://www.appcoda.com) 752 | * [Tutorials Point](http://www.tutorialspoint.com/ios/) 753 | * [Code with Cris](http://codewithchris.com/) 754 | * [Cocoa with Love](http://www.cocoawithlove.com/) 755 | * [Cocoa is my Girlfriend](http://www.cimgf.com/) 756 | * [Code School - Try Objective-C](http://tryobjectivec.codeschool.com/) 757 | * [Brian Advent youtube channel](https://www.youtube.com/channel/UCysEngjfeIYapEER9K8aikw/videos) - Swift tutorials Youtube Channel. :large_orange_diamond: 758 | * [RAYWENDERLICH](http://www.raywenderlich.com/tutorials) - Tutorials for developers and gamers 759 | * [Ry’s Objective-C Tutorial](http://rypress.com/tutorials/objective-c/index) 760 | * [Mike Ash](https://www.mikeash.com/pyblog/) 761 | * [Big Nerd Ranch](https://www.bignerdranch.com/blog/categories/ios/) :large_orange_diamond: 762 | * [Tuts+](http://code.tutsplus.com/categories/ios-sdk) :large_orange_diamond: 763 | * [iOS-Blog](http://ios-blog.co.uk/) :large_orange_diamond: 764 | * [Thinkster](https://thinkster.io/a-better-way-to-learn-swift) :large_orange_diamond: 765 | * [Swift Education](https://github.com/swifteducation) - A community of educators sharing materials for teaching Swift and app development. :large_orange_diamond: 766 | * [Cocoa Dev Central](http://cocoadevcentral.com) 767 | * [Use Your Loaf](http://useyourloaf.com) 768 | * [Swift Tutorials by Jameson Quave](http://jamesonquave.com/blog/tutorials/) :large_orange_diamond: 769 | 770 | ### iOS UI Template 771 | * [App Icon Template](http://appicontemplate.com/ios8/) 772 | * [iOS 8 GUI PSD Template](http://www.teehanlax.com/tools/iphone/) 773 | * [iOS UI Design Kit](http://www.invisionapp.com/tethr) 774 | * [iOS Design Guidelines](http://iosdesign.ivomynttinen.com/) 775 | 776 | ### Prototyping 777 | * [FluidUI](https://www.fluidui.com) 778 | * [Proto.io](https://proto.io/) 779 | * [Framer](http://framerjs.com/) 780 | * [Pixate](http://www.pixate.com/) 781 | * [Principle](http://principleformac.com) 782 | 783 | # Twitter 784 | * [@objcio](https://twitter.com/objcio) 785 | * [@nshipster](https://twitter.com/NSHipster) 786 | * [@CocoaPods](https://twitter.com/CocoaPods) 787 | * [@CocoaPodsFeed](https://twitter.com/CocoaPodsFeed) 788 | * [@RubyMotion](https://twitter.com/RubyMotion) 789 | 790 | # Facebook Groups 791 | * [HH iOS](https://www.facebook.com/groups/hhios/) 792 | * [Sketch - Official group](https://www.facebook.com/groups/sketchformac/) 793 | * [Design-Code](https://www.facebook.com/groups/designcode/) 794 | * [Sketch-Design.io](https://www.facebook.com/groups/sketchdesignio) 795 | * [Origami Community](https://www.facebook.com/groups/origami.community/) 796 | * [Framer JS](https://www.facebook.com/groups/framerjs/) 797 | 798 | # Podcasts 799 | * [The Ray Wenderlich Podcast](http://www.raywenderlich.com/rwpodcast) 800 | * [Debug](http://www.imore.com/debug) 801 | * [iDeveloper](http://ideveloper.co/) 802 | * [App Story](http://www.appstorypodcast.com) 803 | * [Mobile Couch](http://mobilecouch.co/) 804 | * [iOS Bytes](https://iosbytes.codeschool.com/) 805 | 806 | # Books 807 | * [Programming with Objective-C by Apple](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/ProgrammingWithObjectiveC.pdf) 808 | * [Object-Oriented Programming with Objective-C by Apple](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/OOP_ObjC/OOP_ObjC.pdf) 809 | * [The Swift Programming Language by Apple](https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11) :large_orange_diamond: 810 | * [Using Swift with Cocoa and Objective C by Apple](https://itunes.apple.com/us/book/using-swift-cocoa-objective/id888894773?mt=11) :large_orange_diamond: 811 | * [iOS Programming: The Big Nerd Ranch Guide by Christian Keur, Aaron Hillegass, Joe Conway](https://www.bignerdranch.com/we-write/ios-programming/) 812 | * [Programming in Objective-C by Stephen G. Kochan](http://www.amazon.com/Programming-Objective-C-6th-Developers-Library/dp/0321967607) 813 | * [Your First iOS App by Ash Furrow](https://leanpub.com/your-first-ios-app) 814 | * [The Complete Friday Q & A: Volume 1](https://www.mikeash.com/book.html) 815 | * [Core Data for iOS: Developing Data-Driven Applications for the iPad, iPhone, and iPod touch](http://www.amazon.com/Core-Data-iOS-Data-Driven-Applications/dp/0321670426/) 816 | * [Cocoa Design Patterns](http://www.amazon.com/Cocoa-Design-Patterns-Erik-Buck/dp/0321535022) 817 | 818 | # Other Awesome Lists 819 | Other amazingly awesome lists can be found in the 820 | * [awesome-awesomeness](https://github.com/bayandin/awesome-awesomeness) list. 821 | * [Open Source apps](https://github.com/dkhamsing/open-source-ios-apps) list of open source ios apps 822 | * Awesome-swift 823 | * [@matteocrippa](https://github.com/matteocrippa/awesome-swift) - A collaborative list of awesome swift resources. 824 | * [@Wolg](https://github.com/Wolg/awesome-swift) - A curated list of awesome Swift frameworks, libraries and software. 825 | * [awesome watchkit apps](https://github.com/sanketfirodiya/sample-watchkit-apps) curated list of sample watchkit apps and tutorials. :watch: 826 | * [iOS Learning Resources](https://github.com/sanketfirodiya/iOS-learning-resources) Comprenehensive collection of high quality, frequently updated and well maintained iOS tutorial sites. 827 | * [awesome-ios-animation](https://github.com/sxyx2008/awesome-ios-animation) - A curated list of awesome iOS animation, including Objective-C and Swift libraries. 828 | * [awesome-ios-chart](https://github.com/sxyx2008/awesome-ios-chart) - A curated list of awesome iOS chart libraries, including Objective-C and Swift. 829 | * [awesome-gists](https://github.com/vsouza/awesome-gists#ios) - A list of amazing gists (iOS section). 830 | * [awesome-ios-ui](https://github.com/cjwirth/awesome-ios-ui) - A curated list of awesome iOS UI/UX libraries. 831 | * [iOS App Development on Medium](https://medium.com/ios-os-x-development) - Stories and technical tips about building apps for iOS, Apple Watch, and iPad/iPhone 832 | 833 | # Contributing 834 | [See the guide](https://github.com/vsouza/awesome-ios/blob/master/CONTRIBUTING.md) 835 | 836 | # License 837 | 838 | Public Domain Mark 840 | 841 | 842 | To the extent possible under law, [Vinicius Souza](https://github.com/vsouza) has waived all copyright and related or neighboring rights to this work. 843 | -------------------------------------------------------------------------------- /bin/assets/test-white-list: -------------------------------------------------------------------------------- 1 | # white list 2 | 3 | WL https://nodejs.org 4 | 5 | WL http://amzn.com/0321812182 6 | 7 | WL https://coveralls.io/r/haml/haml 8 | WL [![Coverage Status](https://coveralls.io/repos/lodash/lodash/badge.svg?branch=master&service=github)](https://coveralls.io/github/lodash/lodash?branch=master) 9 | https://coveralls.io 10 | 11 | WL https://discord.gg/0ZcbPKXt5bWJVmUY 12 | 13 | WL http://badge.fury.io/js/react-native 14 | WL https://badge.fury.io/js/react-native.svg 15 | WL https://fury-badge.herokuapp.com/rb/fog.png 16 | 17 | WL http://bit.ly/xkcdapp 18 | 19 | WL http://eepurl.com/VEKCn 20 | 21 | WL https://fb.me/react-0.14.1.js 22 | WL https://fb.me/react-dom-0.14.1.js 23 | 24 | WL http://groups.google.com/group/guard-dev 25 | WL http://groups.google.com/group/ruby-bundler 26 | http://groups.google.com 27 | 28 | WL https://github.com/CocoaPods/CocoaPods-app/releases/new 29 | WL https://github.com/dkhamsing/ios-asset-names/issues/new 30 | WL https://github.com/facebook/react-native.git 31 | WL https://github.com/docker/docker/releases/latest 32 | WL https://github.com/shykes/helloflask/archive/master.tar.gz 33 | WL https://github.com/AFNetworking/AFNetworking/archive/master.zip 34 | WL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh 35 | WL https://github.com/Homebrew/homebrew/tree/master/LICENSE.txt redirects to 36 | https://github.com/vadimg/js_bintrees 37 | https://github.com/Homebrew/homebrew/blob/master/LICENSE.txt 38 | https://github.com/dkhamsing 39 | 40 | https://maven-badges.herokuapp.com/maven-central/com.github.dblock.waffle/waffle-jna 41 | 42 | http://ogp.me/ns/object# 43 | WL http://ogp.me/ns/profile# 44 | 45 | WL http://www.reddit.com/message/compose/?to=juan_potato 46 | 47 | WL https://stackoverflow.com/questions/ask?tags=realm 48 | http://stackoverflow.com/ 49 | http://stackoverflow.com/questions/19280341/create-directory-if-it-doesnt-exist-with-ruby 50 | 51 | WL https://travis-ci.org/facebook/react-native.svg?branch=master 52 | WL https://travis-ci.org/facebook/react-native.png 53 | https://travis-ci.org/facebook/ 54 | 55 | WL https://t.co/an02Vvi8Tl 56 | WL https://t.co/8OSxRtjogQ 57 | 58 | WL https://youtu.be/uuk-5Fur9Nc 59 | https://www.youtube.com/watch?v=uuk-5Fur9Nc&feature=youtu.be 60 | -------------------------------------------------------------------------------- /bin/comments: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.push File.expand_path('../../lib', __FILE__) 3 | 4 | require 'frankenstein/comments' 5 | Comments 6 | -------------------------------------------------------------------------------- /bin/frankenstein: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.push File.expand_path('../../lib', __FILE__) 3 | 4 | require 'frankenstein' 5 | Frankenstein 6 | -------------------------------------------------------------------------------- /bin/issues: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.push File.expand_path('../../lib', __FILE__) 3 | 4 | require 'frankenstein/issues' 5 | Issues 6 | -------------------------------------------------------------------------------- /bin/mergeclose: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.push File.expand_path('../../lib', __FILE__) 3 | 4 | require 'frankenstein/merge' 5 | Merge 6 | -------------------------------------------------------------------------------- /bin/new: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.push File.expand_path('../../lib', __FILE__) 3 | 4 | require 'frankenstein/new' 5 | New 6 | -------------------------------------------------------------------------------- /bin/review: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.push File.expand_path('../../lib', __FILE__) 3 | 4 | require 'frankenstein/review' 5 | Review 6 | -------------------------------------------------------------------------------- /bin/scan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.push File.expand_path('../../lib', __FILE__) 3 | 4 | require 'frankenstein/scan' 5 | Scan 6 | -------------------------------------------------------------------------------- /bin/todo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.push File.expand_path('../../lib', __FILE__) 3 | 4 | require 'frankenstein/todo' 5 | Todo 6 | -------------------------------------------------------------------------------- /frankenstein.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'frankenstein/version' 5 | require 'frankenstein/constants' 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = Frankenstein::PRODUCT 9 | spec.version = Frankenstein::VERSION 10 | spec.authors = ['dkhamsing'] 11 | spec.email = ['dkhamsing8@gmail.com'] 12 | 13 | spec.summary = Frankenstein::SUMMARY 14 | spec.description = Frankenstein::DESCRIPTION 15 | spec.homepage = Frankenstein::PROJECT_URL 16 | spec.license = 'MIT' 17 | 18 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 19 | spec.executables = [spec.name] 20 | spec.bindir = 'bin' 21 | spec.require_paths = ['lib'] 22 | 23 | # dependencies 24 | spec.add_dependency 'awesome_bot', '~> 1.13.6' # validate links 25 | spec.add_dependency 'colored', '~> 1.2' # output 26 | spec.add_dependency 'differ', '~> 0.1.2' # string diff 27 | 28 | spec.add_dependency 'github-readme', '~> 0.1.0.pre' # github 29 | spec.add_dependency 'netrc', '~> 0.11.0' # credentials 30 | spec.add_dependency 'twitter', '~> 5.16' # tweets 31 | spec.add_dependency 'github-trending', '~> 0.2.3' # scan 32 | end 33 | -------------------------------------------------------------------------------- /lib/frankenstein.rb: -------------------------------------------------------------------------------- 1 | require 'frankenstein/cli' 2 | require 'frankenstein/core' 3 | require 'frankenstein/constants' 4 | require 'frankenstein/date' 5 | require 'frankenstein/emoji' 6 | require 'frankenstein/github' 7 | require 'frankenstein/io' 8 | require 'frankenstein/log' 9 | require 'frankenstein/network' 10 | require 'frankenstein/output' 11 | require 'frankenstein/twitter' 12 | require 'frankenstein/usage' 13 | require 'frankenstein/version' 14 | 15 | # Check for live URLs on a page 16 | module Frankenstein 17 | require 'colored' 18 | require 'parallel' 19 | 20 | # logs are stored in FILE_LOG_DIRECTORY 21 | cli_create_log_dir 22 | 23 | # process cli arguments 24 | argv1, argv_flags = ARGV 25 | 26 | if argv1.nil? 27 | usage 28 | exit 29 | end 30 | 31 | argv1 = cli_filter_github(argv1) 32 | filtered = argv1.sub('/', '-') # TODO: filtered should be in core 33 | if core_logs.scan(filtered).count > 0 34 | m = "#{em_logo} there are previous runs for #{argv1.white} in "\ 35 | "#{FILE_LOG_DIRECTORY.green}" 36 | puts m 37 | pattern = "#{FILE_LOG_DIRECTORY}/*#{filtered}.frankenstein" 38 | r = Dir.glob pattern 39 | puts pluralize2 r.count, 'file' 40 | puts r 41 | end 42 | 43 | flag_verbose = cli_log(argv_flags) 44 | log = Frankenstein::Log.new(flag_verbose, argv1) 45 | 46 | file_copy = log.filename(FILE_COPY) 47 | file_updated = log.filename(FILE_UPDATED) 48 | file_redirects = log.filename(FILE_REDIRECTS) 49 | file_log = log.filelog 50 | 51 | option_github_stars_only, 52 | option_head, 53 | flag_fetch_github_stars, 54 | flag_minimize_output, 55 | argv1_is_http, 56 | found_file_content, 57 | number_of_threads = cli_process(argv1, argv_flags, log) 58 | 59 | log.verbose "Number of threads: #{number_of_threads}" 60 | 61 | if flag_fetch_github_stars && !github_creds 62 | log.error GITHUB_CREDS_ERROR 63 | exit(1) 64 | end 65 | 66 | # start 67 | elapsed_time_start = Time.now 68 | 69 | log.file_write "$ #{PRODUCT} #{ARGV.join ' '} \n\n" 70 | 71 | the_url, 72 | readme, 73 | content = 74 | if argv1_is_http || found_file_content 75 | argv1 76 | else 77 | if github_creds 78 | c = github_client 79 | repo = argv1 80 | 81 | begin 82 | default_branch = github_default_branch c, repo 83 | rescue StandardError => e 84 | puts "Error getting branch #{e}".red 85 | exit 86 | end 87 | 88 | readme, content = github_readme c, repo 89 | 90 | if readme.nil? 91 | log.error content 92 | io_record_visits(argv1, 93 | 0, 94 | [], 95 | [], 96 | log.identifier, 97 | nil) 98 | exit 99 | end 100 | 101 | m, raw_info = github_repo_info_client c, repo, default_branch 102 | log.add m 103 | 104 | [repo, readme, content] 105 | else 106 | log.verbose 'Attempt to get default branch (unauthenticated)' 107 | 108 | begin 109 | default_branch = github_default_branch c, repo 110 | rescue StandardError => e 111 | puts "Error getting branch #{e}".red 112 | exit 113 | end 114 | 115 | message, parsed = github_repo_unauthenticated(argv1, log) 116 | log.verbose "Parsed message: #{message}" 117 | 118 | if github_repo_error message 119 | log.error github_repo_error_message message, argv1 120 | exit(1) 121 | elsif message.include? 'API rate limit exceeded' 122 | log.error "GitHub #{message}" 123 | log.add 'Finding readme...' 124 | 125 | default_branch = 'master' 126 | net_find_github_url_readme(argv1, default_branch) 127 | else 128 | default_branch = parsed['default_branch'] 129 | m, raw_info = github_repo_json_info(parsed, default_branch, argv1) 130 | log.add m 131 | github_readme_unauthenticated(argv1, log) 132 | end # if message .. 133 | end 134 | end # if argv1_is_http .. 135 | 136 | if the_url.nil? 137 | puts "No content found for #{argv1.white}" 138 | exit 139 | end 140 | 141 | log.verbose "Readme found: #{readme}" 142 | 143 | m = "#{em_logo} Processing links for ".white 144 | m << the_url.blue 145 | m << ' ...'.white 146 | log.add m 147 | 148 | if content.nil? 149 | content = if found_file_content 150 | found_file_content 151 | else 152 | code = net_status the_url 153 | log.verbose "#{the_url} status: #{code}" 154 | 155 | unless code == 200 156 | if argv1_is_http 157 | log.error "url response (status code: #{code})" 158 | exit(1) 159 | else 160 | log.error 'could not find readme in master branch'.white 161 | exit 162 | end 163 | end 164 | 165 | content = net_get(the_url).body 166 | content 167 | end 168 | end 169 | File.open(file_copy, 'w') { |f| f.write(content) } 170 | 171 | links_to_check, links_found = core_find_links content 172 | log.verbose "Links found: #{links_found}" 173 | 174 | failures, redirects = 175 | core_run( 176 | elapsed_time_start, 177 | log, 178 | links_to_check, 179 | argv1, 180 | number_of_threads, 181 | default_branch, 182 | readme, 183 | option_github_stars_only, 184 | option_head, 185 | flag_minimize_output, 186 | flag_fetch_github_stars, 187 | file_redirects, 188 | file_updated, 189 | file_copy, 190 | file_log) 191 | 192 | io_record_review argv1 193 | 194 | # TODO: check for twitter creds 195 | 196 | if github_creds && !(ARGV.include? OPTION_SKIP) && io_record_pull_check(argv1) 197 | option_happy = '-h' 198 | option_gist = 'g' 199 | option_tweet = 't' 200 | option_pull = 'p' 201 | option_w = 'w' 202 | 203 | done = nil 204 | while done.nil? 205 | m = "\nNext? (" 206 | m << "#{option_pull.white}ull request | " if 207 | (redirects.count > 0) && (readme.nil? == false) 208 | m << "white list #{option_w.white}= | " if redirects.count > 0 209 | m << "#{option_gist.white}ist | "\ 210 | "#{option_tweet.white}weet [#{option_happy.white}] [message] | "\ 211 | 'enter to end) ' 212 | print m 213 | user_input = STDIN.gets.chomp 214 | 215 | if user_input.include? option_w 216 | wl = user_input.sub("#{option_w}=", '') 217 | list = wl.split '^' 218 | 219 | list.each do |x| # TODO: this looks like it could be improved 220 | redirects.reject! do |hash| 221 | key, * = hash.first 222 | key.include? x 223 | end 224 | end 225 | 226 | core_process_redirects( 227 | file_redirects, 228 | file_copy, 229 | file_updated, 230 | redirects, 231 | log) 232 | next 233 | end 234 | 235 | if user_input.downcase == option_pull 236 | unless github_repo_exist(github_client, argv1) 237 | log.error "#{argv1.red} is not a repo" 238 | exit(1) 239 | end 240 | 241 | log.add "\nCreating pull request on GitHub for #{argv1} ...".white 242 | desc = github_pull_description(redirects, failures) 243 | p = github_pull_request(argv1, default_branch, readme, file_updated, 244 | desc, log) 245 | log.add "Pull request created: #{p.blue}".white 246 | io_record_pull(argv1, p) 247 | done = true 248 | elsif (user_input.downcase == option_gist) || 249 | (user_input.include? option_tweet) 250 | done = true 251 | gist_url, * = Frankenstein.github_create_gist file_log, true 252 | 253 | if user_input.include? option_tweet 254 | client = twitter_client 255 | message = user_input.sub(option_tweet, '').sub(option_happy, '').strip 256 | 257 | happy = user_input.include? option_happy 258 | tweet = Frankenstein.twitter_frankenstein_tweet(argv1, gist_url, 259 | message, happy) 260 | t = client.update tweet 261 | twitter_log Frankenstein.twitter_tweet_url(client, t) 262 | end # if user_input.downcase == 't' 263 | else # any other key 264 | done = true 265 | end # user input == y 266 | end # while 267 | end # if github_creds 268 | 269 | io_record_visits( 270 | argv1, 271 | links_to_check.count, 272 | redirects, 273 | failures, 274 | log.identifier, 275 | raw_info) unless found_file_content 276 | 277 | exit(1) if failures.count > 0 278 | end # module 279 | -------------------------------------------------------------------------------- /lib/frankenstein/announce.rb: -------------------------------------------------------------------------------- 1 | # Create a gist from log 2 | module Announce 3 | require 'colored' 4 | require 'frankenstein/github' 5 | require 'frankenstein/twitter' 6 | 7 | PRODUCT = 'announce' 8 | OPTION_HAPPY = '-h' 9 | OPTION_TWEET = 'tweet' 10 | 11 | LEADING_SPACE = ' ' 12 | SPACE_ARGS = "#{LEADING_SPACE} \t\t" 13 | 14 | argv1 = ARGV[0] 15 | if argv1.nil? 16 | m = "#{PRODUCT.blue} #{'Upload a log to GitHub gist'.white} "\ 17 | "\n#{LEADING_SPACE}"\ 18 | "Usage: #{PRODUCT.blue} <#{'file'.white}> "\ 19 | "[#{OPTION_HAPPY.white}] "\ 20 | "[#{OPTION_TWEET.white} message] \n"\ 21 | "#{SPACE_ARGS} #{OPTION_TWEET} \t Tweet a message (optional) \n"\ 22 | "#{SPACE_ARGS} #{OPTION_HAPPY} \t Make the tweet happy 🎉 \n"\ 23 | "\n#{LEADING_SPACE}"\ 24 | "#{PRODUCT} requires credentials in .netrc " 25 | puts m 26 | puts "\n" 27 | exit 28 | end 29 | 30 | unless Frankenstein.github_creds 31 | puts Frankenstein::GITHUB_CREDS_ERROR 32 | exit(1) 33 | end 34 | 35 | option_tweet = ARGV.include? OPTION_TWEET 36 | 37 | if option_tweet 38 | creds = Frankenstein.twitter_config 39 | if creds.nil? 40 | puts 'Missing Twitter credentials in .netrc'.red 41 | exit(1) 42 | end 43 | end 44 | 45 | unless File.exist? argv1 46 | puts "#{PRODUCT.red} File #{argv1.white} does not exist" 47 | exit(1) 48 | end 49 | 50 | gist_url, filename = Frankenstein.github_create_gist argv1, true 51 | 52 | if option_tweet 53 | client = Frankenstein.twitter_client 54 | 55 | separator = '-' 56 | a = filename.split separator 57 | project = a[4..a.count].join(separator).sub(separator, '/') 58 | fr = 'frankenstein' 59 | project = project.gsub(fr, '')[0...-1] if project.include? fr 60 | 61 | user_input = ARGV[2..ARGV.count].join ' ' 62 | 63 | happy = ARGV.include? OPTION_HAPPY 64 | tweet = Frankenstein.twitter_frankenstein_tweet(project, gist_url, 65 | user_input, happy) 66 | t = client.update tweet 67 | 68 | Frankenstein.twitter_log Frankenstein.twitter_tweet_url(client, t) 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/frankenstein/cli.rb: -------------------------------------------------------------------------------- 1 | # Process cli: command line interface 2 | module Frankenstein 3 | class << self 4 | def cli_create_log_dir 5 | Dir.mkdir FILE_LOG_DIRECTORY unless File.exist?(FILE_LOG_DIRECTORY) 6 | end 7 | 8 | def cli_filter_github(argv1) 9 | repo = argv1.gsub(%r{https?:\/\/github.com\/}, '') 10 | ((repo.count '/').to_i == 1) ? repo : argv1 11 | end 12 | 13 | def cli_get_github(option_github_stars_only, argv_flags) 14 | if option_github_stars_only 15 | true 16 | else 17 | argv_flags.to_s.include? FLAG_GITHUB_STARS 18 | end 19 | end 20 | 21 | def cli_option_value(name, separator, log) 22 | temp = cli_option_value_raw name, separator, log 23 | temp ? temp.to_i : nil 24 | end 25 | 26 | def cli_option_value_raw(name, separator, log) 27 | regex = "#{name}#{separator}" 28 | log.verbose "Regular expression: #{regex}" 29 | temp = ARGV.find { |e| /#{regex}/ =~ e } 30 | 31 | temp ? temp.split(separator)[1] : nil 32 | end 33 | 34 | def cli_all_flags 35 | [ 36 | FLAG_MINIMIZE_OUTPUT, 37 | FLAG_VERBOSE, 38 | FLAG_GITHUB_STARS 39 | ] 40 | end 41 | 42 | def cli_log(argv_flags) 43 | argv_flags.to_s.include? FLAG_VERBOSE 44 | end 45 | 46 | def cli_process_flags(option_github_stars_only, argv_flags) 47 | flag_fetch_github_stars = cli_get_github(option_github_stars_only, 48 | argv_flags) 49 | flag_minimize_output = argv_flags.to_s.include? FLAG_MINIMIZE_OUTPUT 50 | 51 | [ 52 | flag_fetch_github_stars, 53 | flag_minimize_output 54 | ] 55 | end 56 | 57 | def cli_process_options 58 | option_github_stars_only = ARGV.include? OPTION_STARS 59 | option_head = ARGV.include? OPTION_HEAD 60 | 61 | [ 62 | option_github_stars_only, 63 | option_head 64 | ] 65 | end 66 | 67 | def cli_process_misc(argv1, log) 68 | argv1_is_http = argv1.match(/^http/) 69 | 70 | unless argv1_is_http 71 | begin 72 | found_file_content = File.read(argv1) 73 | rescue StandardError => e 74 | log.verbose "Not a file: #{e.to_s.red}" 75 | end 76 | end 77 | 78 | number_of_threads = cli_option_value OPTION_THREADS, SEPARATOR, log 79 | number_of_threads = DEFAULT_NUMBER_OF_THREADS if number_of_threads.nil? 80 | 81 | [ 82 | argv1_is_http, 83 | found_file_content, 84 | number_of_threads 85 | ] 86 | end 87 | 88 | def cli_process(argv1, argv_flags, log) 89 | argv1_is_http, 90 | found_file_content, 91 | number_of_threads = cli_process_misc(argv1, log) 92 | 93 | option_github_stars_only, 94 | option_head = cli_process_options 95 | 96 | flag_fetch_github_stars, 97 | flag_minimize_output = cli_process_flags(option_github_stars_only, 98 | argv_flags) 99 | 100 | [ 101 | option_github_stars_only, 102 | option_head, 103 | flag_fetch_github_stars, 104 | flag_minimize_output, 105 | argv1_is_http, 106 | found_file_content, 107 | number_of_threads 108 | ] 109 | end 110 | 111 | def cli_prompt(option_pull, option_white_list) 112 | m = "\nCreate pull request? (#{option_pull.white}ull request | "\ 113 | "white list #{option_white_list.white}=s1^s2.. | "\ 114 | 'enter to end) ' 115 | print m 116 | STDIN.gets.chomp 117 | end 118 | end # class 119 | end 120 | -------------------------------------------------------------------------------- /lib/frankenstein/comments.rb: -------------------------------------------------------------------------------- 1 | # Gather comments... 2 | module Comments 3 | require 'colored' 4 | 5 | require 'frankenstein/constants' 6 | require 'frankenstein/github' 7 | require 'frankenstein/io' 8 | require 'frankenstein/output' 9 | 10 | FILE = "#{Frankenstein::FILE_LOG_DIRECTORY}/franken_comments.json" 11 | 12 | PRODUCT = 'comments' 13 | PRODUCT_DESCRIPTION = 'Gather comments on merged issues' 14 | 15 | OPTION_GET = 'get' 16 | OPTION_READ = 'read' 17 | OPTION_ALL = 'all' 18 | 19 | LEADING_SPACE = ' ' 20 | 21 | unless Frankenstein.github_creds 22 | puts Frankenstein::GITHUB_CREDS_ERROR 23 | exit(1) 24 | end 25 | 26 | argv1, argv2 = ARGV 27 | 28 | if argv1.nil? 29 | u_p = PRODUCT.blue 30 | u_d = PRODUCT_DESCRIPTION.white 31 | o_g = OPTION_GET.white 32 | o_r = OPTION_READ.white 33 | o_a = OPTION_ALL.white 34 | m = "#{u_p} #{u_d} \n"\ 35 | "#{LEADING_SPACE}"\ 36 | "Usage: #{u_p} #{o_g} - get latest\n"\ 37 | "#{LEADING_SPACE} "\ 38 | "#{u_p} #{o_g} #{o_a} - get all \n"\ 39 | "#{LEADING_SPACE} "\ 40 | "#{u_p} #{o_r} " 41 | puts m 42 | exit 43 | end 44 | 45 | if File.exist? FILE 46 | puts "Reading #{FILE.white}" 47 | json = Frankenstein.io_json_read FILE 48 | end 49 | 50 | if argv1 == OPTION_READ 51 | exit unless File.exist? FILE 52 | 53 | json.reject { |y| y.values[0]['number_of_comments'] == 0 } 54 | .each_with_index do |x, i| 55 | project = x.keys[0] 56 | puts "#{i + 1} #{project.white}" 57 | 58 | c = x.values[0]['comments'] 59 | c.each do |d| 60 | # pp c 61 | cr = d['created_at'] 62 | up = d['updated_at'] 63 | 64 | m = " #{d['body']} — @#{d['login']}" 65 | 66 | if cr == up 67 | puts m 68 | else 69 | puts m + ' (updated)'.red 70 | end 71 | end 72 | end 73 | exit 74 | end 75 | 76 | class << self 77 | def project_from_issue(i) 78 | x = pull_url i 79 | project_from_url x 80 | end 81 | 82 | def project_from_url(x) 83 | x.gsub(/\/pull.*$/, '').sub('https://github.com/', '') 84 | end 85 | 86 | def pull_url(y) 87 | y[:pull_request][:html_url] 88 | end 89 | end 90 | 91 | if argv1 == OPTION_GET 92 | puts '> Creating GitHub client' 93 | client = Frankenstein.github_client 94 | client.auto_paginate = true if argv2 == OPTION_ALL 95 | 96 | state = 'merged' 97 | puts "> Getting #{state} issues" 98 | i = Frankenstein.github_issues client, state 99 | 100 | issues = i[:items] 101 | puts "Got #{issues.count} items " 102 | 103 | unless json.nil? 104 | keys = json.map { |x| x.keys[0] } 105 | 106 | issues = issues.reject do |y| 107 | project = project_from_issue y 108 | condition = keys.include? project 109 | puts "Skipping #{project.white}" if condition 110 | 111 | condition 112 | end 113 | end 114 | 115 | map = issues.each_with_index.map do |y, i2| 116 | project = project_from_issue y 117 | number = y[:number] 118 | 119 | begin 120 | comments = client.issue_comments project, number 121 | # pp comments 122 | rescue StandardError => e 123 | puts "Error getting comments #{e}".red 124 | next 125 | end 126 | 127 | m = "#{i2} #{project.white} #{comments.count} comments" 128 | puts m 129 | 130 | cm = comments.map do |z| 131 | { 132 | login: z['user']['login'], 133 | created_at: z['created_at'], 134 | updated_at: z['updated_at'], 135 | body: z['body'] 136 | } 137 | end 138 | 139 | sleep 0.8 140 | { project => 141 | { 142 | url: y, 143 | comments: cm, 144 | number_of_comments: comments.count 145 | } 146 | } 147 | end 148 | 149 | f = map 150 | f = map.concat json unless json.nil? 151 | Frankenstein.io_json_write FILE, f 152 | puts "Wrote log to #{FILE.white}" 153 | end 154 | end # module 155 | -------------------------------------------------------------------------------- /lib/frankenstein/constants.rb: -------------------------------------------------------------------------------- 1 | # Constants 2 | module Frankenstein 3 | require 'frankenstein/readmes' 4 | require 'frankenstein/whitelist' 5 | 6 | ARGV1_FILE = 'file' 7 | ARGV1_GITHUB_REPO = 'github repo' 8 | ARGV1_URL = 'url' 9 | 10 | DEFAULT_NUMBER_OF_THREADS = 10 11 | 12 | FILE_LOG_DIRECTORY = 'logs' 13 | 14 | FILE_COPY = 'copy' 15 | FILE_REPO = "#{FILE_LOG_DIRECTORY}/franken_repos.json" 16 | FILE_REDIRECTS = 'redirects' 17 | FILE_TODO = "#{FILE_LOG_DIRECTORY}/franken_todo.json" 18 | FILE_UPDATED = 'updated' 19 | FILE_VISITS = "#{FILE_LOG_DIRECTORY}/franken_visits.json" 20 | 21 | FLAG_GITHUB_STARS = 'z' 22 | FLAG_MINIMIZE_OUTPUT = 'm' 23 | FLAG_VERBOSE = 'v' 24 | 25 | FLAG_GITHUB_USAGE = 'Get GitHub repo info' 26 | FLAG_MINIMIZE_USAGE = 'Minimized result output' 27 | FLAG_VERBOSE_USAGE = 'Verbose output' 28 | 29 | OPTION_HEAD = 'head' 30 | OPTION_SKIP = 'silent' 31 | OPTION_STARS = 'repo' 32 | OPTION_THREADS = 'threads' 33 | 34 | PRODUCT = 'frankenstein' 35 | 36 | PROJECT_URL = 'https://github.com/dkhamsing/frankenstein' 37 | 38 | PULL_REQUEST_COMMIT_MESSAGE = 'Update README URLs based on HTTP redirects' 39 | PULL_REQUEST_TITLE = PULL_REQUEST_COMMIT_MESSAGE 40 | PULL_REQUEST_DESCRIPTION = "Created with #{PROJECT_URL}" 41 | 42 | SEPARATOR = '=' 43 | 44 | WHITE_LIST_STATUS = -1 45 | end 46 | -------------------------------------------------------------------------------- /lib/frankenstein/core.rb: -------------------------------------------------------------------------------- 1 | # Frankenstein core 2 | module Frankenstein 3 | require 'awesome_bot' 4 | require 'parallel' 5 | require 'colored' 6 | require 'json' 7 | 8 | require 'frankenstein/constants' 9 | require 'frankenstein/diff' 10 | require 'frankenstein/github' 11 | require 'frankenstein/io' 12 | require 'frankenstein/log' 13 | require 'frankenstein/network' 14 | require 'frankenstein/output' 15 | require 'frankenstein/twitter' 16 | 17 | class << self 18 | def core_find_links(content) 19 | links_found = AwesomeBot.links_find content 20 | links_to_check = AwesomeBot.links_filter links_found 21 | 22 | [links_to_check, links_found] 23 | end 24 | 25 | def core_links_to_check(_, json_url) 26 | c = net_get json_url 27 | json = JSON.parse c.body 28 | 29 | title = json['title'] 30 | body = json['body'] 31 | content = "#{title} #{body}" 32 | 33 | links_to_check, * = core_find_links content 34 | 35 | match = content.match /.*\/.*/ 36 | matched = match.to_s.split ' ' 37 | matched = matched.select { |x| x.include? '/' } 38 | 39 | links_to_check = links_to_check + matched if matched.count > 0 40 | 41 | [links_to_check.uniq, json] 42 | end 43 | 44 | def core_logs 45 | d = Dir.entries(FILE_LOG_DIRECTORY) 46 | d.join ' ' 47 | end 48 | 49 | def check_comments(client, project, number, logo) 50 | puts "\n#{logo} Checking comments ..." 51 | comments = client.issue_comments project, number 52 | puts 'No comments' if comments.count == 0 53 | 54 | comments.each do |c| 55 | u = '@' << c[:user][:login] 56 | m = "\n#{u.white}: #{c[:body]} " 57 | puts m 58 | end unless comments == 0 59 | # end 60 | end 61 | 62 | def delete_fork(client, fork, logo) 63 | puts "\n#{logo} Deleting fork #{fork} ..." 64 | client.delete_repository fork 65 | end 66 | 67 | def finish(tweet, clean_pull_url) 68 | puts tweet 69 | 70 | client = twitter_client 71 | t = client.update tweet 72 | 73 | puts "\nTweet sent #{twitter_tweet_url(client, t).blue}" 74 | 75 | core_open_safari clean_pull_url 76 | end 77 | 78 | def core_open_safari(url, verbose = true) 79 | puts "\nOpening Safari ..." if verbose 80 | system("open -a Safari #{url}") 81 | end 82 | 83 | def core_merge(argv1) 84 | puts "#{em_logo} Parsing input #{argv1.white} ..." 85 | clean_pull_url = argv1.gsub(/#.*$/, '') 86 | puts clean_pull_url 87 | number = clean_pull_url.gsub(/.*pull\//, '') 88 | puts number 89 | proj = clean_pull_url.gsub(/\/pull.*$/, '').sub('https://github.com/', '') 90 | puts proj 91 | username = proj.gsub(/\/.*$/, '') 92 | puts username 93 | fork = proj.sub(username, github_netrc_username) 94 | puts fork 95 | 96 | puts "\n#{em_logo} Creating GitHub client" 97 | client = github_client 98 | 99 | puts "\n#{em_logo} Getting changes ... " 100 | f = client.pull_files proj, number 101 | changes = f[0][:additions] 102 | m = 'Found '\ 103 | "#{pluralize2 changes, 'change'} " 104 | puts m 105 | 106 | puts "\n#{em_logo} Checking merge status for #{proj.white} ..." 107 | merged = client.pull_merged? proj, number 108 | puts 'Pull request was merged 🎉'.green if merged == true 109 | 110 | puts "\n#{em_logo} Checking pull request status ..." unless merged == true 111 | state = client.pull(proj, number)[:state] 112 | puts 'Pull request was closed 😡' if state == 'closed' && merged == false 113 | 114 | check_comments(client, proj, number, em_logo) 115 | 116 | puts '' 117 | if merged == true || state == 'closed' 118 | result = delete_fork(client, fork, em_logo) 119 | # if result == false 120 | # puts "The fork #{fork.red} has already been deleted.." 121 | # core_open_safari clean_pull_url 122 | # exit 123 | # end 124 | 125 | puts "\n#{em_logo} Crafting tweet ... \n\n" 126 | if (merged == true) 127 | t = "#{em_logo}#{clean_pull_url} was merged with "\ 128 | "#{pluralize2 changes, 'change'} "\ 129 | "#{twitter_random_happy_emoji}" 130 | else # closed :-( 131 | t = "#{em_logo}This pull request with "\ 132 | "#{pluralize2 changes, 'change'} "\ 133 | "looked pretty good ¯/_(ツ)_/¯ #{clean_pull_url}/files" 134 | end 135 | 136 | finish t, clean_pull_url 137 | else 138 | puts 'Pull request is still open 📗' 139 | end 140 | end 141 | 142 | def core_process_redirects( 143 | file_redirects, 144 | file_copy, 145 | file_updated, 146 | redirects, 147 | log) 148 | io_json_write file_redirects, redirects 149 | 150 | m = "\n#{em_status_yellow} #{pluralize2 redirects.count, 'redirect'}" 151 | log.add m.yellow 152 | 153 | log.verbose "Replacing redirects in temp file #{file_updated}.." 154 | File.open(file_copy, 'a+') do |f| 155 | original = f.read 156 | replaced = original 157 | 158 | puts " #{redirects.uniq.count} unique" if 159 | redirects.uniq.count != redirects.count 160 | 161 | redirects.uniq.each do |hash| 162 | original, redirect = hash.first 163 | log.add "#{original.yellow} redirects to \n#{redirect.yellow} " 164 | 165 | changes = Differ.diff_by_word(redirect, original).changes 166 | changes.each do |c| 167 | if c.delete == '' 168 | m = "#{c.insert.green} was added" 169 | elsif c.insert == '' 170 | m = "#{c.delete.red} was removed" 171 | else 172 | m = "#{c.delete.red} was replaced by #{c.insert.green}" 173 | end 174 | log.add " #{m}" 175 | 176 | puts ' ' << '!!!'.red_on_yellow if 177 | (c.insert != 'https') && (original.include? '//github.com/') 178 | end 179 | 180 | log.add '' 181 | 182 | replaced = replaced.sub original, redirect 183 | end # redirects.each 184 | 185 | File.open(file_updated, 'w') do |ff| 186 | puts "Wrote redirects replaced to #{file_updated.white}" 187 | ff.write(replaced) 188 | end 189 | end # File.open(FILE_TEMP, 'a+') { |f| 190 | end 191 | 192 | def core_run( 193 | elapsed_time_start, 194 | log, 195 | links_to_check, 196 | argv1, 197 | number_of_threads, 198 | branch, 199 | readme, 200 | option_github_stars_only, 201 | option_head, 202 | flag_minimize_output, 203 | flag_fetch_github_stars, 204 | file_redirects, 205 | file_updated, 206 | file_copy, 207 | file_log) 208 | log.verbose '🔎 Links found: '.white 209 | log.verbose links_to_check 210 | 211 | if links_to_check.count == 0 212 | log.error_header 'no links found' 213 | else 214 | unless option_github_stars_only 215 | m = "🔎 Checking #{pluralize2 links_to_check.count, 'link'}" 216 | log.add m 217 | end 218 | 219 | misc = [] 220 | issues = [] 221 | failures = [] 222 | redirects = [] 223 | unless option_github_stars_only 224 | _, links_to_check = links_to_check.partition do |link| 225 | condition = in_white_list link, log 226 | output_status(flag_minimize_output, 227 | WHITE_LIST_STATUS, link, log) if condition 228 | condition 229 | end 230 | 231 | AwesomeBot.statuses(links_to_check, number_of_threads, 10, true) do |s, u, h| 232 | # puts "#{s} #{s.class} #{u}".red 233 | if s.class != Fixnum 234 | log.error "Getting link #{u.white} #{s}" 235 | 236 | issue = "#{em_status_red} #{s} #{u}" 237 | issues.push(issue) 238 | failures.push(issue) 239 | next 240 | end 241 | 242 | output_status(flag_minimize_output, s, u, log) 243 | 244 | next if s == 200 245 | 246 | if s >= 500 247 | misc.push(u) 248 | elsif s >= 400 249 | failures.push "#{status_glyph s, u, log} #{s} #{u}" 250 | elsif s >= 300 251 | redirect = h['location'] 252 | log.verbose "#{u} was redirected to \n#{redirect}".yellow 253 | 254 | if redirect.nil? 255 | log.add "#{em_mad} No redirect found for #{u}" 256 | elsif redirect == u 257 | log.add "😓 Redirect is the same for #{u}" 258 | else 259 | if in_white_list2 REDIRECTED_WHITE_LIST, redirect, log 260 | log.add "#{em_status_white} #{u.white} is in the "\ 261 | 'redirect white list' 262 | next 263 | end 264 | 265 | issues.push "#{status_glyph s, u, log} #{s} #{u}" 266 | redirects.push u => redirect 267 | end 268 | end # if s 269 | end # end AwesomeBot 270 | 271 | if failures.count > 0 272 | log.add "\n#{pluralize2 failures.count, 'failure'}" 273 | log.add failures 274 | end 275 | 276 | output_issues issues, links_to_check, log 277 | output_misc(misc, log) if misc.count > 0 278 | end # unless option_github_stars_only 279 | 280 | if flag_fetch_github_stars 281 | github_repos = github_get_repos links_to_check 282 | log.verbose github_repos 283 | 284 | if github_repos.count == 0 285 | log.add 'No GitHub repos found'.white 286 | else 287 | m = "\n🔎 Getting information for "\ 288 | "#{pluralize2 github_repos.count, 'GitHub repo'}".white 289 | log.add m 290 | 291 | github_repos_info(github_repos, number_of_threads, github_client, 292 | log) 293 | end # if github_repos.count == 0 294 | end # flag_fetch_github_stars 295 | end # if links_to_check.count==0 296 | 297 | redirects = [] if redirects.nil? 298 | 299 | core_process_redirects( 300 | file_redirects, 301 | file_copy, 302 | file_updated, 303 | redirects, 304 | log) if redirects.count > 0 305 | 306 | puts "Wrote log to #{file_log.white}" 307 | 308 | elapsed_seconds = Time.now - elapsed_time_start 309 | log.verbose "Elapsed time in seconds: #{elapsed_seconds}" 310 | 311 | m = "\n🕐 Time elapsed: ".white << elapsed(elapsed_seconds) 312 | log.add m 313 | 314 | log.add '' 315 | failures = [] if failures.nil? 316 | if failures.count == 0 317 | log.add "#{em_logo} No failures for #{argv1.blue}".white 318 | else 319 | if failures.count > 0 320 | m = "#{em_status_red} #{pluralize2 failures.count, 'failure'} "\ 321 | "for #{argv1.blue}".red 322 | log.add m 323 | end 324 | end 325 | 326 | log.file_write "\nCreated with #{PROJECT_URL} "\ 327 | "#{Time.now.strftime('%b %d, %Y')} \n" 328 | 329 | f = "#{FILE_LOG_DIRECTORY}/#{log.identifier}-stats" 330 | f << "-r#{redirects.count}" if redirects.count > 0 331 | f << "-f#{failures.count}" if failures.count > 0 332 | File.open(f, 'w') { |ff| ff.write("#{PRODUCT} info for #{argv1}") } 333 | 334 | f = "#{FILE_LOG_DIRECTORY}/#{log.identifier}-info" 335 | hash = { 336 | repo: argv1, 337 | branch: branch, 338 | readme: readme 339 | } 340 | io_json_write f, hash 341 | 342 | [failures, redirects] 343 | end 344 | 345 | def core_scan_time_ago(t, index, argv1) 346 | today = Time.now.to_i 347 | seconds = today - t 348 | 349 | minutes = seconds / 60 350 | hour = minutes / 60 351 | minutes -= (60 * hour) if hour > 0 352 | day = hour / 24 353 | 354 | m = "#{index + 1} Skipping #{argv1.white}, run " 355 | if day > 0 356 | m << "#{pluralize2 day, 'day'}" 357 | else 358 | if hour > 0 359 | m << "#{hour}h #{minutes}m" 360 | else 361 | m << "#{pluralize2 minutes, 'minute'}" 362 | end 363 | end 364 | m << ' ago' 365 | 366 | m 367 | end 368 | 369 | def core_scan(argv_1, force = false) 370 | c = File.read argv_1 371 | links, * = core_find_links c 372 | r = github_get_repos links 373 | puts "Scanning #{pluralize2(r.count, 'repo').white}" 374 | 375 | flag_verbose = false 376 | number_of_threads = 10 377 | logs = core_logs 378 | 379 | begin 380 | records = io_json_read FILE_VISITS 381 | rescue => e 382 | puts "Error reading visits.json #{e}".red 383 | records = nil 384 | end 385 | 386 | r.each.with_index do |argv1, index| 387 | unless force 388 | if logs.include? argv1.sub('/', '-') 389 | match = argv1.sub('/', '-') 390 | t = core_logs.match(/(.){22}(#{match})/) 391 | epoch = t[0].gsub(/-.*/, '').to_i 392 | 393 | puts core_scan_time_ago epoch, index, argv1 394 | next 395 | elseif !records.nil? 396 | if records.keys.include? argv1 397 | d = records[argv1]['log'].last['date'] 398 | t = (Time.parse d).to_i 399 | 400 | puts core_scan_time_ago t, index, argv1 401 | next 402 | end 403 | end 404 | end 405 | 406 | elapsed_time_start = Time.now 407 | 408 | log = Log.new(flag_verbose, argv1) 409 | 410 | file_copy = log.filename(FILE_COPY) 411 | file_updated = log.filename(FILE_UPDATED) 412 | file_redirects = log.filename(FILE_REDIRECTS) 413 | file_log = log.filelog 414 | 415 | if github_creds 416 | c = github_client 417 | repo = argv1 418 | 419 | b = github_default_branch c, repo 420 | readme, content = github_readme c, repo 421 | 422 | if readme.nil? 423 | print 'Error '.red 424 | puts content 425 | io_record_visits(argv1, 426 | 0, 427 | [], 428 | [], 429 | log.identifier, 430 | nil) 431 | next 432 | end 433 | 434 | m, raw_info = github_repo_info_client c, repo, b 435 | log.add m 436 | else 437 | message, parsed = github_repo_unauthenticated(argv1, log) 438 | 439 | if github_repo_error message 440 | log.error github_repo_error_message message, argv1 441 | next 442 | elsif message.include? 'API rate limit exceeded' 443 | log.error "GitHub #{message}" 444 | log.add 'Finding readme...' 445 | 446 | b = 'master' 447 | the_url, readme = net_find_github_url_readme(argv1, b) 448 | else 449 | b = parsed['default_branch'] 450 | m, raw_info = github_repo_json_info(parsed, b, argv1) 451 | log.add m 452 | 453 | the_url, 454 | readme, 455 | content = github_readme_unauthenticated(argv1, log) 456 | 457 | if the_url.nil? 458 | puts "No content found for #{argv1.white}" 459 | next 460 | end 461 | end # if message .. 462 | end 463 | 464 | content = net_get(the_url).body if content.nil? 465 | File.open(file_copy, 'w') { |f| f.write(content) } 466 | 467 | links_to_check, * = core_find_links content 468 | 469 | f, r = core_run( 470 | elapsed_time_start, 471 | log, 472 | links_to_check, 473 | argv1, 474 | number_of_threads, 475 | b, 476 | readme, 477 | false, # option_github_stars_only, 478 | true, # option_head, 479 | false, # flag_minimize_output, 480 | false, # flag_fetch_github_stars, 481 | file_redirects, 482 | file_updated, 483 | file_copy, 484 | file_log) 485 | 486 | io_record_visits(argv1, 487 | links_to_check.count, 488 | r, 489 | f, 490 | log.identifier, 491 | raw_info) 492 | end # r.each 493 | end 494 | 495 | def core_todo_add(item) 496 | f = FILE_TODO 497 | log = if File.exist? f 498 | io_json_read f 499 | else 500 | [] 501 | end 502 | 503 | log.push item 504 | log = log.uniq 505 | 506 | io_json_write f, log 507 | 508 | log 509 | end 510 | end # class 511 | end 512 | -------------------------------------------------------------------------------- /lib/frankenstein/date.rb: -------------------------------------------------------------------------------- 1 | # Dates 2 | module Frankenstein 3 | class << self 4 | def number_of_days_since_raw(date) 5 | days = difference date 6 | months = days / 30 7 | 8 | [text_from(days, months), days] 9 | end 10 | 11 | def number_of_days_since(date) 12 | m, days = number_of_days_since_raw date 13 | 14 | case 15 | when days < 10 16 | return m.green 17 | when days < 30 18 | return m.blue 19 | when days < 60 20 | return m 21 | else 22 | return m.red 23 | end 24 | end 25 | 26 | def difference(date) 27 | time = Time.new 28 | return 0 if date.nil? 29 | 30 | difference = -((date - time) / 60 / 60 / 24) 31 | difference.round(0) 32 | end 33 | 34 | def text_from(days, months) 35 | m = 'last updated ' 36 | case 37 | when days == 0 38 | m << 'today' 39 | when days < 100 40 | m << "#{pluralize2 days, 'day'} ago" 41 | else 42 | m << "#{pluralize2 months, 'month'} ago" 43 | end 44 | 45 | m 46 | end 47 | end # class 48 | end 49 | -------------------------------------------------------------------------------- /lib/frankenstein/diff.rb: -------------------------------------------------------------------------------- 1 | # String diffs 2 | module Frankenstein 3 | require 'differ' 4 | require 'differ/string' 5 | 6 | # Diff change 7 | class Differ::Diff 8 | def changes 9 | @raw.reject { |e| e.is_a? String } 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/frankenstein/emoji.rb: -------------------------------------------------------------------------------- 1 | # Emojis 2 | module Frankenstein 3 | class << self 4 | def em_heat 5 | '🔥 ' 6 | end 7 | 8 | def em_logo 9 | '🏃 ' 10 | end 11 | 12 | def em_mad 13 | '😡 ' 14 | end 15 | 16 | def em_star 17 | '⭐️ ' 18 | end 19 | 20 | def em_status_red 21 | '🔴 ' 22 | end 23 | 24 | def em_status_white 25 | '⚪ ' 26 | end 27 | 28 | def em_status_yellow 29 | '🔶 ' 30 | end 31 | 32 | def em_sunglasses 33 | '😎 ' 34 | end 35 | end # class 36 | end 37 | -------------------------------------------------------------------------------- /lib/frankenstein/github.rb: -------------------------------------------------------------------------------- 1 | # GitHub helper 2 | module Frankenstein 3 | GITHUB_API_BASE = 'https://api.github.com/' 4 | GITHUB_RAW_CONTENT_URL = 'https://raw.githubusercontent.com/' 5 | 6 | GITHUB_CREDS_ERROR = 'Missing GitHub credentials in .netrc' 7 | 8 | NETRC_GITHUB_MACHINE = 'api.github.com' 9 | 10 | class << self 11 | require 'octokit' 12 | require 'netrc' 13 | require 'json' 14 | require 'github-readme' 15 | 16 | require 'frankenstein/date' 17 | require 'frankenstein/diff' 18 | require 'frankenstein/emoji' 19 | # require 'pp' 20 | 21 | def github_client 22 | Octokit::Client.new(netrc: true) 23 | end 24 | 25 | def github_create_gist(file, public) 26 | separator = '/' 27 | filename = file.split(separator)[1] if file.include? separator 28 | 29 | puts "🏃 Creating a gist for #{filename.white}" 30 | 31 | gputs 'Reading content' 32 | c = File.read(file) 33 | c = c.gsub('[34m', '').gsub('[0m', '').gsub('[37m', '').gsub('[32m', '') 34 | .gsub('[33m', '').gsub('[31m', '') 35 | 36 | gputs 'Creating GitHub client' 37 | client = github_client 38 | gist_file_name = filename.split('-')[4..-1].join '-' 39 | payload = { public: public, 40 | files: { gist_file_name => { content: c } } 41 | } 42 | 43 | gputs 'Client creating gist' 44 | r = client.create_gist(payload) 45 | 46 | html_url = r[:html_url] 47 | gputs "🎉 gist created: #{html_url.white}" 48 | 49 | [html_url, filename] 50 | end 51 | 52 | def github_default_branch(client, repo) 53 | r = github_repo client, repo 54 | r['default_branch'] 55 | end 56 | 57 | def github_fork(client, repo) 58 | client.fork(repo) 59 | end 60 | 61 | def github_netrc 62 | n = Netrc.read 63 | n[NETRC_GITHUB_MACHINE] 64 | end 65 | 66 | def github_creds 67 | !(github_netrc.nil?) 68 | end 69 | 70 | def github_issues(client, state, page = nil) 71 | q = "is:#{state} is:pr author:#{github_netrc_username}" 72 | 73 | if page.nil? 74 | client.search_issues q 75 | else 76 | client.search_issues q, per_page: page 77 | end 78 | end 79 | 80 | def github_netrc_username 81 | n = github_netrc 82 | n[0] 83 | end 84 | 85 | def github_pull_heading(kind) 86 | r = "\n\n### #{kind}Corrected URLs \n"\ 87 | "Was | Now \n"\ 88 | "--- | --- \n" 89 | r 90 | end 91 | 92 | MATCH = '://github' 93 | def github_pull_description(redirects, _) 94 | pr_desc = PULL_REQUEST_DESCRIPTION 95 | 96 | # sort by url 97 | redirects = redirects.uniq.sort_by { |r| r.keys[0] } 98 | 99 | https, redirects = redirects.partition do |hash| 100 | original, redirect = hash.first 101 | changes = Differ.diff_by_word(redirect, original).changes 102 | c = changes[0] 103 | next if changes.count > 1 104 | next if c.class == NilClass 105 | (c.delete == 'http') && (c.insert == 'https') 106 | end 107 | 108 | github, rest = 109 | redirects.partition { |r| r.keys[0].downcase.include? MATCH } 110 | if github.count > 0 111 | h = github_pull_heading 'GitHub ' 112 | pr_desc << h 113 | 114 | github.each do |hash| 115 | key, array = hash.first 116 | pr_desc << "#{key} | #{array} \n" unless key == array 117 | end 118 | end 119 | 120 | if https.count > 0 121 | h = github_pull_heading 'HTTPS ' 122 | pr_desc << h 123 | 124 | https.each do |hash| 125 | key, array = hash.first 126 | pr_desc << "#{key} | #{array} \n" unless key == array 127 | end 128 | end 129 | 130 | if rest.count > 0 131 | h = if (https.count == 0) && (github.count == 0) 132 | github_pull_heading '' 133 | else 134 | github_pull_heading 'Other ' 135 | end 136 | 137 | pr_desc << h 138 | 139 | rest.each do |hash| 140 | key, array = hash.first 141 | pr_desc << "#{key} | #{array} \n" unless key == array 142 | end 143 | end 144 | 145 | # commented out because failures are not always correct when using 146 | # head requests 147 | 148 | # if failures.count > 0 149 | # pr_desc << "\n### URLS could not be reached\n" 150 | # failures.each do |y| 151 | # pr_desc << "\n - #{y}" 152 | # # puts y 153 | # end 154 | # end 155 | 156 | pr_desc 157 | end 158 | 159 | def github_pull_request(repo, branch, readme, filename, description, log) 160 | forker = github_netrc_username 161 | fork = repo.gsub(%r{.*\/}, "#{forker}/") 162 | log.verbose "Fork: #{fork}" 163 | 164 | github = github_client 165 | 166 | # fork 167 | puts "Forking to #{fork.white}..." 168 | forked_repo = nil 169 | while forked_repo.nil? 170 | forked_repo = github_fork(github, repo) 171 | sleep 2 172 | log.verbose 'Forking repo.. sleep' 173 | end 174 | 175 | # commit change 176 | puts 'Committing change...' 177 | ref = "heads/#{branch}" 178 | 179 | # commit to github via http://mattgreensmith.net/2013/08/08/commit-directly-to-github-via-api-with-octokit/ 180 | puts '(Getting ref...)' 181 | begin 182 | githubref = github.ref(fork, ref) 183 | 184 | rescue StandardError => e 185 | puts "Error: #{e}".red 186 | delay = 3 187 | puts "Trying again in #{delay} seconds...".red 188 | sleep delay 189 | githubref = github.ref(fork, ref) 190 | end 191 | 192 | sha_latest_commit = githubref.object.sha 193 | sha_base_tree = github.commit(fork, sha_latest_commit).commit.tree.sha 194 | file_name = readme 195 | my_content = File.read(filename) 196 | 197 | blob_sha = github.create_blob(fork, Base64.encode64(my_content), 'base64') 198 | sha_new_tree = github.create_tree(fork, 199 | [{ path: file_name, 200 | mode: '100644', 201 | type: 'blob', 202 | sha: blob_sha }], 203 | base_tree: sha_base_tree).sha 204 | commit_message = PULL_REQUEST_COMMIT_MESSAGE 205 | sha_new_commit = github.create_commit(fork, 206 | commit_message, 207 | sha_new_tree, 208 | sha_latest_commit).sha 209 | updated_ref = github.update_ref(fork, ref, sha_new_commit) 210 | log.verbose "Updated ref: #{updated_ref}" 211 | log.verbose "Sent commit to fork #{fork}" 212 | 213 | # create pull request 214 | puts 'Opening pull request...' 215 | head = "#{forker}:#{branch}" 216 | log.verbose "Set head to #{head}" 217 | 218 | begin 219 | created = github.create_pull_request(repo, 220 | branch, 221 | head, 222 | PULL_REQUEST_TITLE, 223 | description) 224 | return created[:html_url] 225 | rescue StandardError => e 226 | puts 'Could not create pull request'.red 227 | puts "error #{e}".red 228 | return nil 229 | end 230 | end 231 | 232 | def github_readme(client, repo) 233 | r = GitHubReadme::get repo, client 234 | 235 | e = r['error'] 236 | return [nil, e] unless e.nil? 237 | 238 | name = r['name'] 239 | readme = r['readme'] 240 | [name, readme] 241 | end 242 | 243 | def github_readme_unauthenticated(argv1, log) 244 | json_url = GITHUB_API_BASE + 'repos/' + argv1 + '/readme' 245 | log.verbose "Endpoint: #{json_url}" 246 | log.add "Finding readme for #{argv1.white}" 247 | 248 | body = net_get(json_url).body 249 | log.verbose body 250 | 251 | parsed = JSON.parse(body) 252 | # pp parsed 253 | readme = parsed['name'] 254 | url = parsed['download_url'] 255 | content = parsed['content'] 256 | 257 | decoded = Base64.decode64 content unless content.nil? 258 | 259 | # TODO: this could run out of api calls.. ? 260 | 261 | [url, readme, decoded] 262 | end 263 | 264 | def github_repo(client, repo) 265 | client.repo(repo) 266 | end 267 | 268 | def github_repo_error(message) 269 | message == 'Not Found' || message == 'Moved Permanently' 270 | end 271 | 272 | def github_repo_error_message(message, argv1) 273 | m = "Retrieving repo #{argv1} " 274 | "#{m.red} #{message.downcase}" 275 | end 276 | 277 | def github_repo_info_client(client, repo, default_branch) 278 | parsed = github_repo client, repo 279 | 280 | repo_description = parsed['description'] 281 | repo_stars = parsed['stargazers_count'] 282 | repo_pushed_at = parsed['pushed_at'].to_s 283 | repo_language = parsed['language'] 284 | puts repo_pushed_at 285 | 286 | _, days = number_of_days_since_raw(Time.parse repo_pushed_at) 287 | 288 | raw_updated = if days == 0 289 | 'today' 290 | else 291 | "#{pluralize2 days, 'day'} ago" 292 | end 293 | raw = { 294 | description: repo_description, 295 | stars: repo_stars, 296 | language: repo_language, 297 | pushed_at: repo_pushed_at, 298 | updated: raw_updated 299 | } 300 | 301 | repo_updated = number_of_days_since(Time.parse repo_pushed_at) 302 | 303 | m = "Found: #{default_branch.white} for "\ 304 | "#{repo} — "\ 305 | "#{repo_description} — #{repo_stars}#{em_star} "\ 306 | "— #{repo_updated}" 307 | [m, raw] 308 | end 309 | 310 | def github_repo_info(gh_repo, name) 311 | count = gh_repo.stargazers_count 312 | pushed_at = gh_repo.pushed_at 313 | repo_updated = number_of_days_since pushed_at 314 | message = "#{em_star} #{count} #{name} #{heat_index count} " 315 | message << repo_updated 316 | 317 | h = { repo: name, count: count, pushed_at: pushed_at } 318 | 319 | [message, h] 320 | end 321 | 322 | def github_repo_json_info(parsed, default_branch, argv1_is_github_repo) 323 | repo_description = parsed['description'] 324 | repo_stars = parsed['stargazers_count'] 325 | repo_pushed_at = parsed['pushed_at'] 326 | _, days = number_of_days_since_raw(Time.parse repo_pushed_at) 327 | 328 | raw_updated = if days == 0 329 | 'today' 330 | else 331 | "#{pluralize2 days, 'day'} ago" 332 | end 333 | raw = { 334 | description: repo_description, 335 | stars: repo_stars, 336 | pushed_at: repo_pushed_at, 337 | updated: raw_updated 338 | } 339 | 340 | repo_updated = number_of_days_since(Time.parse repo_pushed_at) 341 | 342 | m = "Found: #{default_branch.white} for "\ 343 | "#{argv1_is_github_repo} — "\ 344 | "#{repo_description} — #{repo_stars}#{em_star} "\ 345 | "— #{repo_updated}" 346 | [m, raw] 347 | end 348 | 349 | def github_repo_exist(client, repo) 350 | client.repository?(repo) 351 | end 352 | 353 | def github_repo_unauthenticated(argv1, log) 354 | # github api has a rate limit of 60 unauthenticated requests per hour 355 | # https://developer.github.com/v3/#rate-limiting 356 | json_url = GITHUB_API_BASE + 'repos/' + argv1 357 | log.verbose "Endpoint: #{json_url}" 358 | log.add "Finding default branch for #{argv1.white}" 359 | 360 | body = net_get(json_url).body 361 | log.verbose body 362 | 363 | parsed = JSON.parse(body) 364 | message = parsed['message'] 365 | message = '' if message.nil? 366 | 367 | [message, parsed] 368 | end 369 | 370 | def github_get_repos(l) 371 | gmatch = 'github.com' 372 | l.select { |m| (m.to_s.downcase.include? gmatch) && (m.count('/') == 4) } 373 | .map { |url| url.split('.com/')[1] } 374 | .reject { |x| (x.include? '.') || (x.include? '#') } 375 | .uniq 376 | end 377 | 378 | def github_repos_info(github_repos, number_of_threads, client, log) 379 | repos_info = [] 380 | Parallel.each(github_repos, in_threads: number_of_threads) do |repo| 381 | log.verbose "Attempting to get info for #{repo.white}" 382 | 383 | begin 384 | gh_repo = github_repo(client, repo) 385 | rescue StandardError => e 386 | log.error "Getting repo for #{repo.white} #{e.message.red}" 387 | next 388 | end 389 | 390 | m, hash = github_repo_info(gh_repo, repo) 391 | repos_info.push(hash) 392 | 393 | only_old = false 394 | if (only_old) 395 | d = difference hash[:pushed_at] 396 | log.add m if d > (365*2) # 2 years 397 | else 398 | log.add m 399 | end 400 | 401 | log.verbose " #{gh_repo.description}" 402 | end 403 | 404 | io_repo_log_json(repos_info, log) unless repos_info.count == 0 405 | end 406 | 407 | def gputs(m) 408 | puts " #{m}" 409 | end 410 | end # class 411 | end 412 | -------------------------------------------------------------------------------- /lib/frankenstein/io.rb: -------------------------------------------------------------------------------- 1 | # I/O 2 | module Frankenstein 3 | KEY_LOG = 'log' 4 | KEY_PULL = 'pull' 5 | KEY_REVIEW = 'review' 6 | KEY_SCAN = 'scan' 7 | KEY_VISIT = 'visit' 8 | 9 | class << self 10 | require 'json' 11 | require 'time' 12 | 13 | def io_records(all) 14 | r = io_json_read Frankenstein::FILE_VISITS 15 | 16 | unless all 17 | r = r.reject do |_, value| 18 | list = value['log'] 19 | 20 | m = list.map do |x| 21 | sc = x['type'] == 'scan' 22 | pu = x['type'] == 'pull' 23 | rev = x['type'] == 'review' 24 | redirects = x['redirects'] == 0 25 | pu || rev || redirects || sc 26 | end 27 | # puts "map = #{m}" 28 | m.include? true 29 | end 30 | end 31 | 32 | r 33 | end 34 | 35 | def io_record_pull_check(repo) 36 | return true unless io_record_pull_exists repo 37 | 38 | print "A pull request exists for #{repo.white}, continue? (y/n) " 39 | user_input = STDIN.gets.chomp 40 | return false if user_input.downcase != 'y' 41 | true 42 | end 43 | 44 | def io_record_pull_exists(repo) 45 | r = io_json_read Frankenstein::FILE_VISITS 46 | r[repo]['log'].each { |x| return true if x['type'] == 'pull' } 47 | false 48 | end 49 | 50 | def io_record_pull(repo, pull_url) 51 | pull = { 52 | type: KEY_PULL, 53 | date: Time.now.utc.iso8601, 54 | pull_url: pull_url 55 | } 56 | if File.exist? FILE_VISITS 57 | r = io_json_read FILE_VISITS 58 | if r.key? repo 59 | hash = r[repo] 60 | list = hash[KEY_LOG] 61 | list.push pull 62 | else 63 | r[repo] = { KEY_LOG => [pull] } 64 | end 65 | 66 | io_json_write FILE_VISITS, r 67 | else 68 | puts "io_record_visits no visits log to record pull for #{repo.red}" 69 | end 70 | end 71 | 72 | def io_record_review(repo) 73 | p = { 74 | type: KEY_REVIEW, 75 | date: Time.now.utc.iso8601 76 | } 77 | 78 | unless File.exist? FILE_VISITS 79 | h = {} 80 | File.open(FILE_VISITS, 'w') {|f| f.puts JSON.generate(h) } 81 | end 82 | 83 | r = io_json_read FILE_VISITS 84 | if r.key? repo 85 | hash = r[repo] 86 | list = hash[KEY_LOG] 87 | list.push p 88 | else 89 | r[repo] = { KEY_LOG => [p] } 90 | end 91 | 92 | io_json_write FILE_VISITS, r 93 | end 94 | 95 | def io_record_scan(username, repos) 96 | item = { 97 | type: KEY_SCAN, 98 | date: Time.now.utc.iso8601, 99 | repos: repos 100 | } 101 | if File.exist? FILE_VISITS 102 | r = io_json_read FILE_VISITS 103 | if r.key? username 104 | hash = r[username] 105 | list = hash[KEY_LOG] 106 | list.push item 107 | else 108 | r[username] = { KEY_LOG => [item] } 109 | end 110 | 111 | io_json_write FILE_VISITS, r 112 | else 113 | puts 'io_record_scan file missing'.red 114 | end 115 | end 116 | 117 | def io_record_visits(repo, number_of_links, redirects, 118 | failures, file, repo_info) 119 | visit = { 120 | type: KEY_VISIT, 121 | date: Time.now.utc.iso8601, 122 | links: number_of_links, 123 | redirects: redirects.count, 124 | failures: failures.count, 125 | file: file 126 | } 127 | visit['info'] = repo_info unless repo_info.nil? 128 | logs = [visit] 129 | 130 | if File.exist? FILE_VISITS 131 | r = io_json_read FILE_VISITS 132 | if r.key? repo 133 | hash = r[repo] 134 | list = hash[KEY_LOG] 135 | list.push visit 136 | else 137 | r[repo] = { KEY_LOG => logs } 138 | end 139 | 140 | io_json_write FILE_VISITS, r 141 | else 142 | hash = { repo => { KEY_LOG => logs } } 143 | 144 | io_json_write FILE_VISITS, hash 145 | end 146 | # puts 'visit recorded ' 147 | end 148 | 149 | def io_repo_log_json(list, log) 150 | log.add "\nWriting repo log ... " 151 | json = if File.exist?(FILE_REPO) 152 | saved = io_json_read FILE_REPO 153 | 154 | list.each do |x| 155 | h = saved.map { |s| s if s['repo'] == x[:repo] }.compact.first 156 | unless h.nil? || x[:count].nil? 157 | difference = x[:count] - h['count'] 158 | m = "#{x[:repo]} count difference: #{difference} #{em_star}" 159 | log.add m unless difference == 0 160 | saved.delete(h) 161 | end 162 | 163 | saved.push(x) 164 | end 165 | 166 | saved 167 | else 168 | list 169 | end 170 | io_json_write FILE_REPO, json 171 | end 172 | 173 | def io_json_read(filename) 174 | c = File.read(filename) 175 | c ? JSON.parse(c) : nil 176 | end 177 | 178 | def io_json_write(filename, content) 179 | json = content.to_json 180 | File.open(filename, 'w') { |f| f.puts(json) } 181 | end 182 | end 183 | end 184 | -------------------------------------------------------------------------------- /lib/frankenstein/issues.rb: -------------------------------------------------------------------------------- 1 | # Extend string 2 | class String 3 | def number? 4 | true if Float(self) 5 | rescue 6 | false 7 | end 8 | end 9 | 10 | # Check GitHub issues 11 | module Issues 12 | require 'colored' 13 | # require 'pp' 14 | 15 | require 'frankenstein/constants' 16 | require 'frankenstein/core' 17 | require 'frankenstein/github' 18 | require 'frankenstein/io' 19 | require 'frankenstein/output' 20 | 21 | PRODUCT = 'issues' 22 | OPTION_MERGE = 'm' 23 | OPTION_OPEN = 'o' 24 | RUN_REPO = 'Run' 25 | 26 | unless Frankenstein.github_creds 27 | puts Frankenstein::GITHUB_CREDS_ERROR 28 | exit(1) 29 | end 30 | 31 | argv1 = ARGV[0] 32 | if argv1 == OPTION_OPEN 33 | puts "Open issues in #{RUN_REPO}" 34 | 35 | client = Frankenstein.github_client 36 | repo = "#{Frankenstein.github_netrc_username}/#{RUN_REPO}" 37 | i = client.list_issues repo 38 | 39 | while i.count > 0 40 | i.each_with_index do |x, index| 41 | url = x['html_url'] 42 | title = x['title'] 43 | puts " #{index + 1} #{url.blue} #{title}" 44 | end 45 | 46 | print '> Enter number to process (or enter to exit): ' 47 | user_input = STDIN.gets.chomp 48 | 49 | exit unless user_input.number? 50 | 51 | index = user_input.to_i - 1 52 | 53 | n = i[index] 54 | i.delete_at(index) 55 | 56 | j = n['url'] 57 | links_to_check, json = Frankenstein.core_links_to_check RUN_REPO, j 58 | 59 | number = json['number'] 60 | 61 | logs = Frankenstein.io_json_read Frankenstein::FILE_VISITS 62 | 63 | files = [] 64 | gists = [] 65 | pulls = [] 66 | left = links_to_check.reject do |x| 67 | m = logs.map do |key, value| 68 | found = false 69 | 70 | if x.include? key 71 | log = value['log'] 72 | found = log.map { |y| y['type'] }.include? 'visit' 73 | 74 | temp = log.select { |y| y['file'] if y['type'] == 'visit' } 75 | if temp.count > 0 76 | file = temp[0]['file'] 77 | dir = Frankenstein::FILE_LOG_DIRECTORY 78 | fr = "#{dir}/#{file}.frankenstein" 79 | files.push key => fr 80 | end 81 | 82 | pull = log.select { |y| y['pull_url'] if y['type'] == 'pull' } 83 | pulls.push pull[0]['pull_url'] if pull.count > 0 84 | end 85 | 86 | found 87 | end 88 | 89 | m.include? true 90 | end 91 | 92 | if left.count == 0 93 | gists = [] 94 | files.each do |hash| 95 | r = hash.keys[0] 96 | f = hash.values[0] 97 | gist_url, * = Frankenstein.github_create_gist f, true 98 | gists.push r => gist_url 99 | sleep 0.5 100 | end 101 | 102 | comment = '`frankenstein` run completed for '\ 103 | "#{Frankenstein.pluralize2 links_to_check.count, 'repo'} \n" 104 | 105 | gists.each do |hash| 106 | r = hash.keys[0] 107 | g = hash.values[0] 108 | t = "- Results for `#{r}`: #{g} \n" 109 | comment << t 110 | end 111 | 112 | pulls.each do |x| 113 | filtered = x.gsub(/\/pull\/.*/, '') 114 | pull_num = x.sub(filtered, '').sub('/pull/', '') 115 | p_text = "#{filtered}/pulls" 116 | comment << "- Created pull request `#{pull_num}` for #{p_text} \n" 117 | end 118 | 119 | client.add_comment repo, number, comment 120 | puts 'Left a comment on GitHub' 121 | 122 | client.close_issue repo, number 123 | puts "GitHub issue #{number} closed" 124 | else 125 | puts 'Still have to run frankenstein for ' 126 | left.each_with_index { |x, idx| puts "#{idx + 1} #{x.white}" } 127 | end # if left.count ... 128 | end # while 129 | exit 130 | end 131 | 132 | state = argv1 == OPTION_MERGE ? 'merged' : 'open' 133 | 134 | puts '> Creating GitHub client' 135 | client = Frankenstein.github_client 136 | 137 | puts '> Getting issues' 138 | i = Frankenstein.github_issues client, state 139 | 140 | count = i[:total_count] 141 | m = Frankenstein.pluralize2 count, 'issue' 142 | puts count == 0 ? 'No issues' : m 143 | 144 | items = i[:items] 145 | items.each_with_index do |x2, i2| 146 | pull_url = x2[:html_url] 147 | m = "#{i2 + 1} #{pull_url.blue}" 148 | puts m 149 | end 150 | 151 | m = "\nUse #{PRODUCT.white} #{OPTION_MERGE.white} for merged pull requests\n"\ 152 | "Use #{PRODUCT.white} #{OPTION_OPEN.white} for open issues in #{RUN_REPO}" 153 | 154 | puts m unless argv1 == OPTION_MERGE 155 | end 156 | -------------------------------------------------------------------------------- /lib/frankenstein/log.rb: -------------------------------------------------------------------------------- 1 | # Logger 2 | module Frankenstein 3 | # Logger 4 | class Log 5 | require 'frankenstein/constants' 6 | 7 | attr_reader :identifier 8 | 9 | def initialize(opt_verbose, argv1) 10 | @verbose = opt_verbose 11 | 12 | epoch = Time.now.to_i 13 | filtered_argv1 = argv1.gsub(%r{https?://}, '').gsub(%r{(\/)}, '-') 14 | today = Date.today 15 | @identifier = "#{epoch}-#{today}-#{filtered_argv1}" 16 | 17 | @file_log = "#{FILE_LOG_DIRECTORY}/#{@identifier}.#{PRODUCT}" 18 | end 19 | 20 | def filelog 21 | @file_log 22 | end 23 | 24 | def filename(extension) 25 | "#{FILE_LOG_DIRECTORY}/#{@identifier}-#{extension}" 26 | end 27 | 28 | def error(message) 29 | add "#{Frankenstein.em_mad} Error: #{message}".red 30 | end 31 | 32 | def error_header(message) 33 | m = "\n📋 #{PRODUCT} results: ".white 34 | my_print m 35 | add message.red 36 | end 37 | 38 | def add(message) 39 | puts message 40 | file_write "#{message}\n" 41 | end 42 | 43 | def my_print(message) 44 | print message 45 | file_write message 46 | end 47 | 48 | def file_write(message) 49 | File.open(@file_log, 'a') { |f| f.write(message) } 50 | end 51 | 52 | def verbose(message) 53 | add message if @verbose 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/frankenstein/merge.rb: -------------------------------------------------------------------------------- 1 | # Check for merged pull request, close fork and send tweet 2 | module Merge 3 | require 'colored' 4 | require 'frankenstein/constants' 5 | require 'frankenstein/core' 6 | require 'frankenstein/github' 7 | require 'frankenstein/output' 8 | 9 | PRODUCT = 'mergeclose' 10 | PRODUCT_DESCRIPTION = 'Automate processing of merged pull requests' 11 | 12 | LEADING_SPACE = ' ' 13 | 14 | argv1 = ARGV[0] 15 | if argv1.nil? 16 | m = "#{PRODUCT.blue} #{PRODUCT_DESCRIPTION.white} "\ 17 | "\n#{LEADING_SPACE}"\ 18 | "Usage: #{PRODUCT.blue} <#{'Pull request URL'.white}> " 19 | puts m 20 | puts "\n" 21 | exit 22 | end 23 | 24 | unless Frankenstein.github_creds 25 | puts Frankenstein::GITHUB_CREDS_ERROR 26 | exit(1) 27 | end 28 | 29 | # TODO: improve checking for pr url 30 | unless argv1.include? 'github.com' 31 | puts 'Oops not a pull request URL'.red 32 | exit 33 | end 34 | 35 | Frankenstein.core_merge argv1 36 | end 37 | -------------------------------------------------------------------------------- /lib/frankenstein/network.rb: -------------------------------------------------------------------------------- 1 | # Networking 2 | module Frankenstein 3 | class << self 4 | def net_find_github_url_readme(repo, branch) 5 | base = "#{GITHUB_RAW_CONTENT_URL}#{repo}/#{branch}/" 6 | readme = README_VARIATIONS.find do |x| 7 | temp = "#{base}#{x}" 8 | net_status(temp) == 200 9 | end 10 | url = "#{base}#{readme}" 11 | 12 | [url, readme] 13 | end 14 | end # class 15 | end 16 | -------------------------------------------------------------------------------- /lib/frankenstein/new.rb: -------------------------------------------------------------------------------- 1 | # Extend string 2 | class String 3 | def number? 4 | true if Float(self) 5 | rescue 6 | false 7 | end 8 | end 9 | 10 | # Check GitHub notifications 11 | module New 12 | require 'colored' 13 | 14 | require 'frankenstein/constants' 15 | require 'frankenstein/core' 16 | require 'frankenstein/network' 17 | require 'frankenstein/github' 18 | require 'frankenstein/output' 19 | 20 | PRODUCT = 'new' 21 | RUN_ISSUES = '/Run/issues/' 22 | LIMIT = 5 23 | 24 | unless Frankenstein.github_creds 25 | puts Frankenstein::GITHUB_CREDS_ERROR 26 | exit(1) 27 | end 28 | 29 | puts '> Creating GitHub client' 30 | client = Frankenstein.github_client 31 | 32 | puts '> Getting notifications' 33 | begin 34 | n = client.notifications 35 | rescue StandardError => e 36 | puts "Error getting notifications #{e}".red 37 | exit 38 | end 39 | 40 | t = Time.new.strftime('%b %d at %I:%M%p') 41 | 42 | if n.count == 0 43 | puts "#{'No notifications'.green} #{t.white}" 44 | exit 45 | end 46 | 47 | m = n.map do |x| 48 | s = x[:subject] 49 | u = s[:url] 50 | u.sub('api.', '').sub('repos/', '').sub('/pulls', '/pull') 51 | # is it better to load `u` and retrieve html_url via json ? 52 | end 53 | 54 | puts "#{Frankenstein.pluralize2 m.count, 'issue'}".white 55 | 56 | m.each_with_index do |x, index| 57 | print "#{index + 1} " 58 | if x.include? RUN_ISSUES 59 | notif = n[index] 60 | subject = notif['subject']['title'] 61 | print x.yellow + ' ' 62 | puts subject 63 | else 64 | puts x.blue 65 | end 66 | 67 | sleep 0.5 68 | 69 | url = m[index] 70 | if url.include? RUN_ISSUES 71 | notif = n[index] 72 | 73 | r = notif['repository']['name'] 74 | user = Frankenstein.github_netrc_username 75 | repo = "#{user}/#{r}" 76 | 77 | j = notif['subject']['url'] 78 | 79 | links_to_check = links_to_check[0,LIMIT] 80 | 81 | links_to_check, json = Frankenstein.core_links_to_check repo, j 82 | 83 | number = json['number'] 84 | links_to_check.each do |x2| 85 | Frankenstein.core_todo_add repo: x 86 | puts "Added #{x2.white} to #{'todo'.blue}" 87 | 88 | comment = "Run request for #{x2} received." 89 | client.add_comment repo, number, comment 90 | 91 | thread = notif['id'] 92 | client.mark_thread_as_read thread 93 | end # end links_to_check.. 94 | else 95 | Frankenstein.core_merge url 96 | 97 | # twitter doesn't like when you tweet too hard 🐦 98 | pause = m.count > 2 ? Random.new.rand(50..70) : 0 99 | puts "Pausing for #{pause}s ..." if pause > 0 100 | sleep pause 101 | end # if url.include? RUN_ISSUES 102 | end # end m.each 103 | 104 | puts "\n#{PRODUCT.white} finished " if n.count > 0 105 | end # module 106 | -------------------------------------------------------------------------------- /lib/frankenstein/output.rb: -------------------------------------------------------------------------------- 1 | # Output 2 | module Frankenstein 3 | class << self 4 | def elapsed(elapsed_seconds) 5 | case 6 | when elapsed_seconds > 60 7 | minutes = (elapsed_seconds / 60).floor 8 | seconds = elapsed_seconds - minutes * 60 9 | m = "#{pluralize2 minutes.round(0), 'minute'}" 10 | m << "#{seconds > 0 ? seconds.round(0).to_s << 's' : ''}" 11 | else 12 | "#{pluralize2 elapsed_seconds.round(2), 'second'}" 13 | end 14 | end 15 | 16 | def heat_index(count) 17 | count = count.to_i 18 | case 19 | when count > 2000 20 | return em_heat << em_heat << em_heat << em_heat << em_heat 21 | when count > 1000 22 | return em_heat << em_heat << em_heat << em_heat 23 | when count > 500 24 | return em_heat << em_heat << em_heat 25 | when count > 200 26 | return em_heat << em_heat 27 | when count > 100 28 | return em_heat 29 | end 30 | end 31 | 32 | def pluralize2(count, text) 33 | "#{count} #{text}#{count > 1 ? 's' : ''}" 34 | end 35 | 36 | def in_white_list(input, log) 37 | w = REGEX_WHITE_LIST + URL_SHORTENER_WHITE_LIST 38 | in_white_list2 w, input, log 39 | end 40 | 41 | def in_white_list2(white_list, input, log) 42 | white_list.each do |regexp| 43 | if input.match(regexp) 44 | log.verbose "#{input} is in white list matching #{regexp}".white 45 | return true 46 | end 47 | end 48 | false 49 | end 50 | 51 | def output_issues(issues, links_to_check, log) 52 | if issues.count > 0 53 | percent = issues.count * 100 / links_to_check.count 54 | m = "#{pluralize2 issues.count, 'issue'} (#{percent.round}%)" 55 | log.error_header m 56 | 57 | m = " #{issues.count} of #{pluralize2 links_to_check.count, 'link'}" 58 | log.add m 59 | 60 | log.add issues 61 | else 62 | m = "\n#{PRODUCT.white} #{'found no errors'.green} for "\ 63 | "#{pluralize2 links_to_check.count, 'link'} #{em_sunglasses}" 64 | log.add m 65 | end 66 | end 67 | 68 | def output_misc(misc, log) 69 | log.add "\n #{pluralize2 misc.count, 'misc item'}".white 70 | end 71 | 72 | def output_status(flag_minimize_output, status, link, log) 73 | if flag_minimize_output 74 | log.my_print status_glyph status, link, log 75 | else 76 | m = status_glyph(status, link, log) 77 | m << ' ' 78 | m << "#{status} " unless 79 | (status == 200) || (status == WHITE_LIST_STATUS) 80 | m << link 81 | log.add m 82 | end 83 | end 84 | 85 | def status_glyph(status, url, log) 86 | return em_status_white if in_white_list url, log 87 | 88 | case 89 | when status == 200 90 | return '✅ ' 91 | when status.to_s.start_with?('3') 92 | return em_status_yellow 93 | when status.to_s.start_with?('4') 94 | return em_status_red 95 | else 96 | return em_status_white 97 | end 98 | end 99 | end # class 100 | end 101 | -------------------------------------------------------------------------------- /lib/frankenstein/readmes.rb: -------------------------------------------------------------------------------- 1 | # README variants 2 | module Frankenstein 3 | README_VARIATIONS = 4 | [ 5 | 'README.md', 6 | 'Readme.md', 7 | 'readme.md', 8 | 'README.markdown', 9 | 'Readme.markdown', 10 | 'README', 11 | 'README.rst', 12 | 'README.asciidoc', 13 | 'README.rdoc', 14 | 'index.html', 15 | 'readme.creole', 16 | 'ReadMe.md', 17 | 'README.mdown', 18 | 'readme.markdown', 19 | 'README.adoc' 20 | ] 21 | end 22 | -------------------------------------------------------------------------------- /lib/frankenstein/review.rb: -------------------------------------------------------------------------------- 1 | # Facilitate creating pull requests to update redirects 2 | module Review 3 | require 'colored' 4 | 5 | require 'frankenstein/cli' 6 | require 'frankenstein/constants' 7 | require 'frankenstein/core' 8 | require 'frankenstein/github' 9 | require 'frankenstein/io' 10 | require 'frankenstein/log' 11 | 12 | PRODUCT = 'review' 13 | PRODUCT_DESCRIPTION = 'Facilitate creating pull requests to update redirects' 14 | 15 | OPTION_ALL = 'all' 16 | OPTION_DONE = 'done' 17 | OPTION_LOG = 'logs' 18 | 19 | argv_1, argv_2, * = ARGV 20 | if argv_1.nil? 21 | o_p = PRODUCT.blue 22 | o_n = 'n'.white 23 | m = "#{o_p} #{PRODUCT_DESCRIPTION.white} \n"\ 24 | "Usage: #{o_p} <#{'file'.white}> \n"\ 25 | " #{o_p} #{OPTION_LOG.blue} \n"\ 26 | " #{o_p} #{OPTION_LOG.blue} #{OPTION_ALL.blue} \n"\ 27 | " #{o_p} #{OPTION_LOG.blue} #{OPTION_DONE.blue} \n"\ 28 | " #{o_p} #{OPTION_LOG.blue} <#{o_n}> or \n"\ 29 | " #{o_p} <#{o_n}> \n"\ 30 | " #{o_p} <#{o_n}> #{OPTION_DONE.blue} \n" 31 | puts m 32 | exit 33 | end 34 | 35 | unless Frankenstein.github_creds 36 | puts Frankenstein::GITHUB_CREDS_ERROR 37 | exit(1) 38 | end 39 | 40 | class << self 41 | def mark_done(repo) 42 | puts "#{repo.white} marked as done" 43 | end 44 | end 45 | 46 | if (argv_1 == OPTION_LOG) && (argv_2 == OPTION_DONE) 47 | r = Frankenstein.io_records(argv_2 == OPTION_ALL) 48 | m = r.map { |key, _| key } 49 | m.each_with_index do |x| 50 | mark_done x 51 | Frankenstein.io_record_review x 52 | end 53 | puts 'Finished' 54 | exit 55 | end 56 | 57 | number = if argv_1 == OPTION_LOG 58 | argv_2.to_i 59 | else 60 | argv_1.to_i 61 | end 62 | 63 | if (argv_1 == OPTION_LOG) && (number == 0) 64 | r = Frankenstein.io_records(argv_2 == OPTION_ALL) 65 | 66 | idx = 0 67 | r.each do |key, value| 68 | idx += 1 69 | puts "#{idx} #{key}".white 70 | list = value['log'] 71 | list.each_with_index do |x, index| 72 | if list.count > 1 73 | head = "#{index + 1}. " 74 | if x['type'] == 'pull' 75 | print head.red 76 | # print 'pull '.red 77 | else 78 | print head 79 | end 80 | end 81 | 82 | type = x['type'] 83 | type = type.red if type == 'pull' 84 | print "#{type} " 85 | 86 | if type == 'visit' 87 | r = x['redirects'] if type 88 | print "#{r.to_s.yellow} " if r > 2 89 | end 90 | puts x 91 | end 92 | end # r.each 93 | 94 | puts 'Log is empty 🎉' if idx == 0 95 | 96 | exit 97 | end 98 | 99 | if number > 0 100 | r = Frankenstein.io_records false 101 | 102 | if number > r.count 103 | puts "No log matching index #{argv_1.red}" 104 | exit 1 105 | end 106 | 107 | idx = 0 108 | s = r.select do |x| 109 | idx += 1 110 | x if idx == number 111 | end 112 | puts s 113 | argv_1 = s.keys[0] 114 | 115 | if argv_2 == OPTION_DONE 116 | mark_done argv_1 117 | Frankenstein.io_record_review argv_1 118 | exit 119 | end 120 | end 121 | 122 | # read repo from log 123 | if argv_1.count('/') == 1 124 | r = Frankenstein.io_json_read Frankenstein::FILE_VISITS 125 | if r.key? argv_1 126 | events = r[argv_1]['log'] 127 | visit = events.select { |x| x['type'] == 'visit' }[0] 128 | argv_1 = visit['file'] 129 | puts argv_1 130 | end 131 | end 132 | 133 | logs_dir = Frankenstein::FILE_LOG_DIRECTORY 134 | file_redirects = "#{logs_dir}/temp-r" 135 | file_updated = "#{logs_dir}/temp-u" 136 | file_log = "#{logs_dir}/temp-log" 137 | 138 | # filter stats files 139 | argv_1 = argv_1.gsub(/-stats.*$/, '') 140 | argv_1 = "#{logs_dir}/#{argv_1}" if 141 | !(argv_1.include? logs_dir) && !(File.exist? argv_1) 142 | 143 | # check the files below exist 144 | file_copy = "#{argv_1}-copy" 145 | file_info = "#{argv_1}-info" 146 | redirects_file = "#{argv_1}-redirects" 147 | 148 | if (!File.exist? file_copy) || 149 | (!File.exist? file_info) || 150 | (!File.exist? redirects_file) 151 | puts 'Error: File(s) missing'.red 152 | exit 153 | end 154 | 155 | puts "Processing for ... \n#{argv_1.white}" 156 | puts "#{file_copy.white} file_copy" 157 | puts "#{file_info.white} file_info" 158 | puts "#{redirects_file.white} redirects_file" 159 | 160 | redirects = Frankenstein.io_json_read redirects_file 161 | 162 | info = Frankenstein.io_json_read file_info 163 | 164 | argv1 = info['repo'] 165 | 166 | default_branch = info['branch'] 167 | readme = info['readme'] 168 | 169 | log = Frankenstein::Log.new(false, file_log) 170 | 171 | if Frankenstein.io_record_pull_check argv1 172 | done = nil 173 | while done.nil? 174 | Frankenstein.core_process_redirects( 175 | file_redirects, 176 | file_copy, 177 | file_updated, 178 | redirects, 179 | log) 180 | 181 | option_pull = 'p' 182 | option_white_list = 'w' 183 | user_input = Frankenstein.cli_prompt option_pull, option_white_list 184 | if user_input.downcase == option_pull 185 | log.add "\nCreating pull request on GitHub for #{argv1} ...".white 186 | 187 | desc = Frankenstein.github_pull_description(redirects, nil) 188 | p = Frankenstein.github_pull_request(argv1, default_branch, readme, 189 | file_updated, desc, log) 190 | log.add "Pull request created: #{p.blue}".white 191 | Frankenstein.io_record_pull(argv1, p) 192 | 193 | done = true 194 | elsif user_input.include? option_white_list 195 | wl = user_input.sub("#{option_white_list}=", '') 196 | list = wl.split '^' 197 | 198 | list.each do |x| # TODO: this looks like it could be improved 199 | redirects.reject! do |hash| 200 | key, * = hash.first 201 | key.include? x 202 | end 203 | end 204 | else 205 | done = true 206 | end 207 | end # end while 208 | end # end if prompt 209 | 210 | Frankenstein.io_record_review argv1 211 | end 212 | -------------------------------------------------------------------------------- /lib/frankenstein/scan.rb: -------------------------------------------------------------------------------- 1 | # Scan for GitHub repos 2 | module Scan 3 | require 'colored' 4 | require 'github-trending' 5 | # require 'pp' 6 | 7 | require 'frankenstein/cli' 8 | require 'frankenstein/constants' 9 | require 'frankenstein/core' 10 | require 'frankenstein/github' 11 | require 'frankenstein/io' 12 | require 'frankenstein/output' 13 | 14 | PRODUCT = 'scan' 15 | PRODUCT_DESCRIPTION = 'Scan for GitHub repos' 16 | 17 | LEADING_SPACE = ' ' 18 | 19 | OPTION_ALL = 'all' 20 | OPTION_POPULAR = 'p' 21 | OPTION_RANDOM = 'r' 22 | OPTION_TREND = 't' 23 | OPTION_TODO = 'todo' 24 | 25 | POPULAR_LANGUAGES = [ 26 | 'java', 27 | 'python', 28 | 'php', 29 | 'csharp', 30 | 'c++', 31 | 'c', 32 | 'javascript', 33 | 'objective-c', 34 | 'swift', 35 | 'r', 36 | 'ruby', 37 | 'perl', 38 | 'matlab', 39 | 'lua', 40 | 'scala', 41 | 'dart', 42 | 'go', 43 | 'rust', 44 | 'coffeescript', 45 | 'julia', 46 | 'elixir', 47 | 'erlang', 48 | 'haskell', 49 | 'unknown' 50 | ] 51 | 52 | class << self 53 | def scan_content(content, allforce = false) 54 | epoch = Time.now.to_i 55 | filename = "#{Frankenstein::FILE_LOG_DIRECTORY}/todo-#{epoch}" 56 | 57 | File.write filename, content 58 | 59 | Frankenstein.core_scan filename, allforce 60 | 61 | File.delete filename 62 | end 63 | 64 | def scan_user(argv_1, all = false) 65 | user = argv_1.sub('@', '') 66 | c = Frankenstein.github_client 67 | c.auto_paginate = true 68 | 69 | begin 70 | u = c.user user 71 | rescue StandardError => e 72 | puts "#{argv_1} is not a valid user: #{e}".red 73 | exit 1 74 | end 75 | 76 | l = u['location'] 77 | repos = u['public_repos'] 78 | m = "Getting repos for #{argv_1.white} " 79 | m << "from #{l.blue}" unless l.nil? 80 | puts m 81 | 82 | puts "#{Frankenstein.pluralize2 repos, 'repo'}" 83 | 84 | r = c.repos(user).reject { |x| x['fork'] } 85 | puts "#{r.count} are not forked" unless r.count == repos 86 | 87 | if all 88 | combined = r 89 | else 90 | puts 'Getting top 5 most popular repos...' 91 | top5 = r.sort_by { |x| x['stargazers_count'] }.reverse.first(5) 92 | .each { |x| puts ' ' << x['full_name'] } 93 | 94 | puts 'Getting latest repos with updates' 95 | recent = r.reject { |x| x['pushed_at'].class == NilClass } 96 | .sort_by { |x| x['pushed_at'] } 97 | .reverse.first(5) 98 | .each { |x| puts ' ' << x['full_name'] } 99 | 100 | combined = recent + top5 101 | end 102 | m = combined.uniq.map { |x| x['full_name'] } 103 | .each_with_index { |x, i| puts "#{i + 1} #{x}" } 104 | 105 | scan_content map_repos(m), all 106 | 107 | Frankenstein.io_record_scan user, m 108 | end 109 | 110 | def map_repos(repos) 111 | mapped = repos.map { |r| "https://github.com/#{r}" } 112 | 113 | m = '' 114 | mapped.each { |r| m << " #{r}" } 115 | 116 | m 117 | end 118 | 119 | def update_left(left, f) 120 | left.delete_at 0 121 | Frankenstein.io_json_write f, left 122 | puts "todo left: #{left.count}" 123 | sleep 1 124 | end 125 | end 126 | 127 | argv_1, argv_2 = ARGV 128 | if argv_1.nil? 129 | a_p = PRODUCT.blue 130 | a_l = 'language'.green 131 | a_t = OPTION_TREND.white 132 | a_f = 'file'.white 133 | a_todo = OPTION_TODO.white 134 | a_r = OPTION_RANDOM.white 135 | a_po = OPTION_POPULAR.white 136 | a_at = '@username'.green 137 | a_a = OPTION_ALL.white 138 | m = "#{a_p} #{PRODUCT_DESCRIPTION.white} \n"\ 139 | "Usage: #{a_p} <#{a_f}> "\ 140 | "\n#{LEADING_SPACE} #{a_p} #{a_t} — scan overall trending repos"\ 141 | "\n#{LEADING_SPACE} #{a_p} #{a_t} [#{a_l}] — scan trending repos for a"\ 142 | ' given language '\ 143 | "\n#{LEADING_SPACE} #{a_p} #{a_r} — scan random trending repos "\ 144 | "\n#{LEADING_SPACE} #{a_p} #{a_po} — scan trending repos for popular "\ 145 | 'languages'\ 146 | "\n#{LEADING_SPACE} #{a_p} #{a_at} — scan top/recent repos for"\ 147 | ' a GitHub user '\ 148 | "\n#{LEADING_SPACE} #{a_p} #{a_at} #{a_a} — (force) scan all repos for"\ 149 | ' a GitHub user '\ 150 | "\n#{LEADING_SPACE} #{a_p} #{a_todo} - scan repos from #{'todo'.blue}" 151 | puts m 152 | puts "\n" 153 | exit 154 | end 155 | 156 | unless Frankenstein.github_creds 157 | puts Frankenstein::GITHUB_CREDS_ERROR 158 | exit(1) 159 | end 160 | 161 | Frankenstein.cli_create_log_dir 162 | 163 | if argv_1.include? '@' 164 | scan_user argv_1, argv_2 == OPTION_ALL 165 | exit 166 | end 167 | 168 | if argv_1 == OPTION_POPULAR 169 | all = [] 170 | pop = POPULAR_LANGUAGES 171 | pop.each_with_index do |p, i| 172 | puts "#{i + 1}/#{pop.count} Getting trending repos for #{p}" 173 | repos = Github::Trending.get p 174 | all += repos 175 | end 176 | 177 | puts 'Getting overall trending repos' 178 | all += Github::Trending.get 179 | 180 | m = all.map(&:name) 181 | scan_content map_repos(m) 182 | exit 183 | end 184 | 185 | if argv_1 == OPTION_TODO 186 | f = Frankenstein::FILE_TODO 187 | todo = Frankenstein.io_json_read f 188 | 189 | left = todo.map(&:dup) 190 | todo.each_with_index do |x| 191 | m = x['repo'] 192 | 193 | if m.include? '@' 194 | scan_user m 195 | update_left left, f 196 | next 197 | end 198 | 199 | m = "https://github.com/#{m}" unless m.include? '://github.com' 200 | puts "Scanning #{m.white}..." 201 | 202 | scan_content m 203 | update_left left, f 204 | end 205 | 206 | puts "Finished scanning #{todo.count} repos" unless todo.count == 0 207 | exit 208 | end 209 | 210 | if (argv_1 == OPTION_TREND) || (argv_1 == OPTION_RANDOM) 211 | puts 'Scanning Trending in GitHub' 212 | 213 | if argv_1 == OPTION_RANDOM 214 | random_language = POPULAR_LANGUAGES.sample 215 | puts "Random Language: #{random_language.white}" 216 | repos = Github::Trending.get random_language 217 | elsif argv_2.nil? 218 | repos = Github::Trending.get 219 | else 220 | puts "Language: #{argv_2.white}" 221 | repos = Github::Trending.get argv_2 222 | end 223 | 224 | scan_content map_repos(repos.map(&:name)) 225 | 226 | exit 227 | end 228 | 229 | unless File.exist? argv_1 230 | puts "#{PRODUCT.red} File #{argv_1.white} does not exist" 231 | exit(1) 232 | end 233 | 234 | Frankenstein.core_scan(argv_1) 235 | end 236 | -------------------------------------------------------------------------------- /lib/frankenstein/todo.rb: -------------------------------------------------------------------------------- 1 | # Add a repo to the frankenstein to do list 2 | module Todo 3 | require 'colored' 4 | 5 | require 'frankenstein/constants' 6 | require 'frankenstein/core' 7 | require 'frankenstein/io' 8 | 9 | PRODUCT = 'todo' 10 | 11 | OPTION_LIST = 'list' 12 | 13 | LEADING_SPACE = ' ' 14 | 15 | argv1 = ARGV[0] 16 | o_p = PRODUCT.blue 17 | if argv1.nil? 18 | o_r = 'repo'.white 19 | o_l = OPTION_LIST.blue 20 | u = 'Add a repo to run frankenstein on later' 21 | m = "#{o_p} #{u.white} \n"\ 22 | "#{LEADING_SPACE} Usage: #{o_p} <#{o_r}> \n"\ 23 | "#{LEADING_SPACE} Usage: #{o_p} #{o_l}" 24 | puts m 25 | exit 26 | end 27 | 28 | if argv1 == OPTION_LIST 29 | l = Frankenstein.io_json_read Frankenstein::FILE_TODO 30 | l.each do |x| 31 | repo = x['repo'] 32 | puts " #{repo.white}" 33 | end 34 | puts "#{o_p} items: #{l.count.to_s.white}" 35 | exit 36 | end 37 | 38 | item = { 39 | repo: argv1 40 | } 41 | 42 | l = Frankenstein.core_todo_add item 43 | 44 | puts "Added #{argv1.white} to #{o_p}" 45 | puts "#{o_p} items: #{l.count.to_s.white}" 46 | end 47 | -------------------------------------------------------------------------------- /lib/frankenstein/twitter.rb: -------------------------------------------------------------------------------- 1 | # Tweets 2 | module Frankenstein 3 | NETRC_TWITTER_CONSUMER = 'consumer.twitter' 4 | NETRC_TWITTER_ACCESS = 'access.twitter' 5 | 6 | HAPPY_EMOJIS = [ 7 | '😎', 8 | '🎉', 9 | '😉', 10 | '😀', 11 | '😄', 12 | '😄', 13 | '☺️', 14 | '🙃', 15 | '👏' 16 | ] 17 | 18 | class << self 19 | require 'twitter' 20 | 21 | def twitter_frankenstein_tweet(project, gist_url, user_input, happy) 22 | t = "🏃 frankenstein for #{project} #{gist_url} #{user_input} " 23 | t << Frankenstein.twitter_random_happy_emoji if happy 24 | t 25 | end 26 | 27 | def twitter_client 28 | config = twitter_config 29 | Twitter::REST::Client.new(config) 30 | end 31 | 32 | def twitter_config 33 | n = Netrc.read 34 | consumer = n[NETRC_TWITTER_CONSUMER] 35 | access = n[NETRC_TWITTER_ACCESS] 36 | 37 | # TODO: netrc missing 38 | 39 | { 40 | consumer_key: consumer[0], 41 | consumer_secret: consumer[1], 42 | access_token: access[0], 43 | access_token_secret: access[1] 44 | } 45 | end 46 | 47 | def twitter_log(message) 48 | puts " 🐦 Tweet sent: #{message.blue}" 49 | end 50 | 51 | def twitter_random_happy_emoji 52 | HAPPY_EMOJIS.sample 53 | end 54 | 55 | def twitter_tweet_url(client, tweet) 56 | username = client.current_user.screen_name 57 | "https://twitter.com/#{username}/status/#{tweet.id}" 58 | end 59 | end # class 60 | end 61 | -------------------------------------------------------------------------------- /lib/frankenstein/usage.rb: -------------------------------------------------------------------------------- 1 | # Usage 2 | module Frankenstein 3 | require 'colored' 4 | 5 | class << self 6 | def all_argv1 7 | "#{ARGV1_URL.magenta}|#{ARGV1_FILE.magenta}|"\ 8 | "#{ARGV1_GITHUB_REPO.magenta}" 9 | end 10 | 11 | def all_tools 12 | tools = %w(announce comments issues mergeclose new review scan todo) 13 | m = tools.map(&:green) 14 | m.join ', ' 15 | end 16 | 17 | def usage 18 | m = "#{em_logo} #{'Check for live URLS on a page'.white} \n" \ 19 | "#{PRODUCT.green} <#{all_argv1}> "\ 20 | "[-#{cli_all_flags.join.blue}] "\ 21 | "[#{OPTION_HEAD.blue}] "\ 22 | "[#{OPTION_STARS.blue}] "\ 23 | "[#{OPTION_THREADS.blue}=d] "\ 24 | "[#{OPTION_SKIP.blue}] "\ 25 | "\n"\ 26 | " #{ARGV1_URL.magenta} \t\t URL for the page \n"\ 27 | " #{ARGV1_GITHUB_REPO.magenta} \t GitHub repository \n"\ 28 | " #{ARGV1_FILE.magenta} \t File on disk \n\n"\ 29 | " #{FLAG_MINIMIZE_OUTPUT.blue} \t\t #{FLAG_MINIMIZE_USAGE} \n"\ 30 | " #{FLAG_VERBOSE.blue} \t\t #{FLAG_VERBOSE_USAGE} \n"\ 31 | " #{FLAG_GITHUB_STARS.blue} \t\t #{FLAG_GITHUB_USAGE} \n"\ 32 | "\n #{OPTION_HEAD.blue} \t Check URLs with head requests 🚀 \n"\ 33 | " #{OPTION_STARS.blue} \t Get GitHub repo info only \n"\ 34 | " #{OPTION_THREADS.blue} \t Number of parallel threads "\ 35 | "(#{DEFAULT_NUMBER_OF_THREADS} is the default) \n"\ 36 | " #{OPTION_SKIP.blue} \t Skip prompt at end of the run \n"\ 37 | "\n#{em_logo} #{'Examples'.white} \n"\ 38 | "$ #{PRODUCT} https://fastlane.tools \n"\ 39 | "$ #{PRODUCT} README.md \n"\ 40 | "$ #{PRODUCT} dkhamsing/open-source-ios-apps -mv threads=10 \n"\ 41 | "$ #{PRODUCT} dkhamsing/open-source-ios-apps stars \n"\ 42 | "\n#{em_logo} \n- Fetching GitHub repo information "\ 43 | "require credentials in .netrc \n"\ 44 | "- More tools: #{all_tools} \n"\ 45 | '- More information: '\ 46 | "#{PROJECT_URL.white.underline}" 47 | 48 | puts m 49 | end 50 | end # class 51 | end 52 | -------------------------------------------------------------------------------- /lib/frankenstein/version.rb: -------------------------------------------------------------------------------- 1 | # Version and description 2 | module Frankenstein 3 | DESCRIPTION = 'Get HTTP status code for links found on a page, optimized '\ 4 | 'for GitHub.' 5 | SUMMARY = 'Check for live URLs on a page.' 6 | VERSION = '0.1.0' 7 | end 8 | -------------------------------------------------------------------------------- /lib/frankenstein/whitelist.rb: -------------------------------------------------------------------------------- 1 | # White list 2 | module Frankenstein 3 | REDIRECTED_WHITE_LIST = 4 | [ 5 | 'clojure.org/.*responseToken=', 6 | '://code.google.com/hosting/moved', 7 | 'raw.githubusercontent.com', 8 | 'Main_Page', 9 | '://nodejs.org/en/', 10 | 'http://www.opentable.com/start/home', 11 | 'https://www.paypal.com/home' 12 | ] 13 | 14 | URL_SHORTENER_WHITE_LIST = 15 | [ 16 | '://aka.ms/', 17 | '://amzn.com/', 18 | '//amzn.to/', 19 | '//bit.ly/', 20 | '//bitly.com', 21 | '//cl.ly/', 22 | '://db.tt/', 23 | '://eepurl.com/', 24 | '://fb.me/', 25 | '://git.io/', 26 | '://goo.gl/', 27 | '//j.mp/', 28 | '//mzl.la/', 29 | '//qr.ae/', 30 | '://t.co/', 31 | '://t.cn/', 32 | '://tinyurl.com/', 33 | '://youtu.be/' 34 | ] 35 | 36 | REGEX_WHITE_LIST = 37 | [ 38 | '://127', 39 | '//developer.apple.com/xcode/download', 40 | '//ci.appveyor.com/api', 41 | '://badge.fury.io/', 42 | '://bitbucket.org/repo/create', 43 | '://cocoapod-badges', 44 | 'codeclimate.com/repos/', 45 | 'codeclimate.*png', 46 | '://coveralls.io/r/', 47 | '://coveralls.io/repos.*(pn|sv)g', 48 | '://discord.gg/', 49 | 'facebook.com/sharer', 50 | 'facebook.com/groups', 51 | '://fury-badge.herokuapp.com/.*png', 52 | '://shop.github.com$', 53 | '://enterprise.github.com/$', 54 | '://github.com/.*/blob/', 55 | '://github.com/.*/fork$', 56 | '://github.com/pulls', 57 | '://github.com/security', 58 | '://github.com/settings', 59 | '://github.com/site', 60 | '://github.com/new', 61 | '://github.com/watching', 62 | '://github.com/.*/new$', 63 | '://github.com.*releases/new', 64 | '://github.com.*releases/download/', 65 | '://github.com.*releases/latest', 66 | '://github.com.*/archive/.*.(gz|zip)', 67 | '://github.com.*\.git$', 68 | '://github.com.*/tree/', 69 | '://github.com/.*/zipball/', 70 | '://github.com/.*/tarball/', 71 | '://github.com/.*/raw/', 72 | '://github.com/.*state=open$', 73 | '//github.com/.*contributors$', 74 | 'github.io', 75 | '//gratipay.com/', 76 | '//heroku.com/deploy', 77 | '//githubbadges.herokuapp', 78 | '//meritbadge.herokuapp.com', 79 | 'imageshack.us/', 80 | 'issuestats.com/', 81 | '://localhost', 82 | '://maven-badges.herokuapp.com/', 83 | '://ogp.me/ns', 84 | '://raw.github.com/', 85 | '://group.google.com', 86 | '://groups.google', 87 | '://i.creativecommons.org/.*png', 88 | '://instagram.com/', 89 | 'paypal.com/cgi-bin/webscr', 90 | 'plus.google.com/share', 91 | 'readthedocs.org', 92 | 'reddit.com', 93 | '://secure.travis-ci.org/.*(pn|sv)g', 94 | 'my.slack.com/services', 95 | 'sourceforge.net/projects/.*/download$', 96 | 'stackexchange.com/a/', 97 | 'stackexchange.com/q/', 98 | '://stackoverflow.com/a/', 99 | '://stackoverflow.com/q/', 100 | '://stackoverflow.com/questions/ask?', 101 | '//swift.org', 102 | '//bugs.swift.org', 103 | '://twitter.com/home', 104 | '://travis-ci.org/.*png', 105 | '://travis-ci.org/.*svg', 106 | '://weibo.com/' 107 | ] 108 | end 109 | --------------------------------------------------------------------------------