newline_hw 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 39b29b562210b7192832f3072f93d105aebbe44b
4
+ data.tar.gz: 551d839f029426a9de65509f082f7ab84c826159
5
+ SHA512:
6
+ metadata.gz: bfd3d1bf736b1b47ae35a98e5e0e5fca04e7ad804207f2b08fddd8f611fc5db67d5310c9db7a7a68ad018c176decc6a1c40ac85f4b8b57315f27bdd425c2261d
7
+ data.tar.gz: 1ae5362a0162f34a7bd487b6d6ddcb3b42efcbbfc1dc27cac775694612e4952db6091474cb400ecb0c3c9e4a9819d4d48a860dd040c18472eb5b4231c669d733
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,4 @@
1
+ Rails:
2
+ Enabled: false
3
+ Style/StringLiterals:
4
+ EnforcedStyle: double_quotes
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at russell@burningpony.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "simplecov"
4
+
5
+ # Specify your gem's dependencies in newline_hw.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Russell Osborne
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # NewlineHw
2
+
3
+ Is a tool to rapidly clone and setup projects in a standard format, this is primarily
4
+ used to improving the speed to review homework turned in in a bootcamp like setting.
5
+
6
+ ## Installation
7
+
8
+ Install from rubygems:
9
+
10
+ $ gem install newline_hw
11
+
12
+ Run to install the config file and chrome integration.
13
+
14
+ $ newline_hw install
15
+
16
+ In your .bashrc / .bash_profile / .zshrc **THIS IS VERY REQUIRED**
17
+
18
+ $ eval "$(newline_hw init)"
19
+
20
+ ## Usage
21
+
22
+ Once the the eval code has been added to your bash or zsh profile you will have access to the `hw` command. This is where the majority of the tools use comes into play.
23
+
24
+ `hw GIT_REPO`
25
+
26
+ Example
27
+
28
+ `hw https://github.com/rposborne/countries`
29
+
30
+ ### What it does
31
+
32
+ _All languages_
33
+
34
+ 1. Clone a git-url/repo/gist/pull-request into ~/theironyard/homework by default using githubname-reponame
35
+ 2. Trigger your editor to open.
36
+ 3. `cd` current shell into new directory
37
+
38
+ _Ruby_
39
+
40
+ 1. Run `bundle install` if `Gemfile` is present
41
+
42
+ _Rails_
43
+
44
+ 1. `bin/rake db:setup`
45
+ 2. Start a rails server / open it in default browser
46
+ 3. `bin/rake test`
47
+ 4. Reown rails server process so everything behaves as expected.
48
+ 5. Tell spring die when it needs to die.
49
+
50
+ _Javascript_
51
+
52
+ 1. Run `npm install` when `package.json` present and no yarn file
53
+ 1. Run `yarn install` when `yarn.lock` present
54
+
55
+ ## TIYO-Assistant Integration
56
+
57
+ This provides a message bus to allow TIYO assistant to send json to our local binary. This is used to open a terminal window (Apple Terminal or iTerm2) using appleScript and start a `hw <submission-id>` command. We use the NewlineCli to backfill data required to run the remainder of the commands.
58
+
59
+ 1. You MUST be on a MAC (hope to drop this in the future)
60
+ 1. You MUST have `newline_cli` installed
61
+ 2. You MUST be running a ruby 2.3 or higher
62
+ 3. You MUST have that ruby either in the system loadpath or use, rbenv, rvm, or chruby.
63
+ 4. THIS IS ALPHA so please send logs.
64
+
65
+ ## Development
66
+
67
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
68
+
69
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
70
+
71
+ ## Contributing
72
+
73
+ Bug reports and pull requests are welcome on GitHub at https://github.com/TIYDC/newline_hw. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
74
+
75
+
76
+ ## License
77
+
78
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "newline_hw"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ begin
11
+ require "pry"
12
+ Pry.start
13
+ rescue LoadError
14
+ require "irb"
15
+ IRB.start
16
+ end
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/newline_hw ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+ require_relative "../lib/newline_hw"
4
+ require_relative "../lib/newline_hw/cli"
5
+ NewlineHw::Cli.start(ARGV)
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+ require "logger"
4
+ require "json"
5
+ require_relative "../lib/newline_hw"
6
+
7
+ logger = Logger.new(File.new(File.expand_path(NewlineHw.config.log_file), "a+"))
8
+
9
+ begin
10
+ NewlineHw::StreamProcessor
11
+ .new($stdin, $stdout, logger: logger).on_message do |msg|
12
+ send_message(NewlineHw::StreamCommandHandler.new(msg).call)
13
+ end
14
+ rescue StandardError => e
15
+ logger.error e
16
+ exit 1
17
+
18
+ rescue SystemExit
19
+ exit 0
20
+ end
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env bash
2
+ # coding: utf-8
3
+
4
+ # Load binaries for rbenv
5
+ if [ -d "$HOME/.rbenv/shims" ]; then
6
+ export PATH=~/.rbenv/shims:$PATH
7
+ # Load RVM into a shell session *as a function*
8
+ elif [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
9
+ # First try to load from a user install
10
+ source "$HOME/.rvm/scripts/rvm"
11
+
12
+ elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
13
+ # Then try to load from a root install
14
+ source "/usr/local/rvm/scripts/rvm"
15
+
16
+ elif [ -f /usr/local/share/chruby/chruby.sh && [ -n "$BASH_VERSION" ] || [ -n "$ZSH_VERSION" ]]; then
17
+ source /usr/local/share/chruby/chruby.sh
18
+ fi
19
+
20
+ exeDir=$(dirname -- "$BASH_SOURCE")
21
+ $exeDir/newline_hw_stream
@@ -0,0 +1,46 @@
1
+ require "json"
2
+ module NewlineHw
3
+ module ChromeManifest
4
+ NAME = "com.theironyard.newlinecli.hw".freeze
5
+
6
+ module_function
7
+
8
+ def binary_path
9
+ File.expand_path File.join(__FILE__, "..", "..", "..", "exe", "newline_hw_stream_shim")
10
+ end
11
+
12
+ def native_messaging_manifest_path
13
+ File.expand_path("~/Library/Application Support/Google/Chrome/NativeMessagingHosts/#{NAME}.json")
14
+ end
15
+
16
+ def generate
17
+ {
18
+ name: NAME,
19
+ description: spec.description,
20
+ path: binary_path,
21
+ type: "stdio",
22
+ allowed_origins: [
23
+ "chrome-extension://fnhanbdccpjnnoohoppkeejljjljihcc/",
24
+ "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"
25
+ ]
26
+ }
27
+ end
28
+
29
+ def write
30
+ create_native_messaging_manifest_directory
31
+
32
+ File.open(native_messaging_manifest_path, "w+") do |f|
33
+ f.write(JSON.pretty_generate(ChromeManifest.generate))
34
+ end
35
+ end
36
+
37
+ private def create_native_messaging_manifest_directory
38
+ return if Dir.exist?(File.dirname(native_messaging_manifest_path))
39
+ Dir.mkdir(File.dirname(native_messaging_manifest_path))
40
+ end
41
+
42
+ private def spec
43
+ @_gemspec ||= Gem::Specification.load("newline_hw.gemspec")
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,60 @@
1
+ require "thor"
2
+ module NewlineHw
3
+ class Cli < Thor
4
+ desc "init", "install the hw function into a current terminal session"
5
+ def init
6
+ puts Shell::Function.cmd
7
+ end
8
+
9
+ desc "config", "edit your config file in your editor"
10
+ def config
11
+ `#{NewlineHw.config.editor} #{NewlineHw::Config::CONFIG_PATH}`
12
+ end
13
+
14
+ desc "install newlinehw config file and chrome adapter",
15
+ "will setup a logging file and a chrome manifest to allow this app to be communicated to by the newline-assistant chrome extension."
16
+ def install
17
+ NewlineHw.make_log_directory
18
+ ChromeManifest.write
19
+ NewlineHw::Config.install_default
20
+ say "Installed a config file to `#{NewlineHw::Config::CONFIG_PATH}`"
21
+ say "Chrome Native Messaging Hook installed for Newline Assistant"
22
+ say ""
23
+ say ("*" * 30) + " YOU MUST!! Add this line to your shell profile " + ("*" * 30), :red
24
+ say '$ eval "$(newline_hw init)"'
25
+ say ("*" * 30), :red
26
+ end
27
+
28
+ desc "setup SUBMISSION_ID",
29
+ "generate a shell command to clone and setup a given SUBMISSION_ID"
30
+ option :editor
31
+ def setup_command(submission_id)
32
+ puts Shell::Setup.new(submission_id, config).cmd
33
+ end
34
+
35
+ desc "run WORKINGDIR", "generate a shell command to run language and project specfic tasks a given SUBMISSION_ID"
36
+ option :editor
37
+ def run_command(working_dir, _submission_id = nil)
38
+ puts Shell::Run.new(working_dir, config).cmd
39
+ end
40
+
41
+ desc "", ""
42
+ option :editor
43
+ option :application
44
+ def gui_trigger(id)
45
+ GuiTrigger.new({ id: id }, config).call
46
+ end
47
+
48
+ no_commands do
49
+ def exit_on_failure?
50
+ true
51
+ end
52
+
53
+ def config
54
+ config = Config.new
55
+ config.update(options)
56
+ config
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,52 @@
1
+ require "yaml"
2
+
3
+ module NewlineHw
4
+ class Config
5
+ CONFIG_PATH = File.expand_path("~/.newline_hw.yaml").freeze
6
+ DEFAULTS = {
7
+ "editor" => "atom",
8
+ "terminal" => "Terminal",
9
+ "launch_editor" => true,
10
+ "homework_dir" => "~/theironyard/homework",
11
+ "log_file" => "~/Library/Logs/newline_hw/newlinehw.log"
12
+ }.freeze
13
+
14
+ def update(options)
15
+ @config = config.merge(options)
16
+ end
17
+
18
+ def config
19
+ @config ||= DEFAULTS.merge(config_file)
20
+ end
21
+
22
+ def config_file
23
+ YAML.load_file(CONFIG_PATH) || {}
24
+ end
25
+
26
+ def editor
27
+ config["editor"]
28
+ end
29
+
30
+ def terminal
31
+ config["terminal"]
32
+ end
33
+
34
+ def log_file
35
+ config["log_file"]
36
+ end
37
+
38
+ def homework_dir
39
+ config["homework_dir"]
40
+ end
41
+
42
+ def launch_editor
43
+ config["launch_editor"]
44
+ end
45
+
46
+ def self.install_default
47
+ File.open(CONFIG_PATH, "w+") do |f|
48
+ f.write DEFAULTS.to_yaml
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,54 @@
1
+ require "uri"
2
+ require "net/http"
3
+ require "json"
4
+ require "openssl"
5
+ require "newline_cli/api"
6
+
7
+ module NewlineHw
8
+ class GuiTrigger
9
+ attr_reader :editor
10
+ def initialize(data, config)
11
+ @newline_submission_id = data["id"]
12
+ @application = config.terminal
13
+ end
14
+
15
+ def application
16
+ return "Terminal".freeze unless %w(iTerm2 Terminal).include?(@application)
17
+ @application
18
+ end
19
+
20
+ def call
21
+ applescript = case application
22
+ when "iTerm2"
23
+ applescript_for_iterm
24
+ else
25
+ applescript_for_terminal
26
+ end
27
+
28
+ { terminal_output: `osascript -e '#{applescript}'` }
29
+ end
30
+
31
+ private def command_to_run_in_tty
32
+ "hw #{@newline_submission_id}"
33
+ end
34
+
35
+ private def applescript_for_terminal
36
+ <<-APPLESCRIPT
37
+ tell application "#{application}" to do script "#{command_to_run_in_tty}"
38
+ tell application "#{application}" to activate
39
+ APPLESCRIPT
40
+ end
41
+
42
+ private def applescript_for_iterm
43
+ <<-APPLESCRIPT
44
+ tell application \"#{application}\"
45
+ set newWindow to (create window with default profile)
46
+
47
+ tell current session of newWindow
48
+ write text "#{command_to_run_in_tty}"
49
+ end tell
50
+ end tell
51
+ APPLESCRIPT
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,52 @@
1
+ # if [[ $# -eq 0 ]] ; then
2
+ # echo 'WARNING: You must provide a git url, pull-request url or a Newline Submission ID.'
3
+ # exit 0
4
+ # fi
5
+ # OUTPUT="$(#{path} run_command $PWD $*)"
6
+ # eval $OUTPUT
7
+
8
+ module NewlineHw
9
+ module Shell
10
+ ##
11
+ # Produce a bash / zsh function to be called by a tty compatible shell
12
+ #
13
+ # WARNING: all bash lines below must be terminated in a semicolon, line
14
+ # endings do not survive being passed through the heredoc correctly for the
15
+ # shell to interpreter correctly.
16
+ module Function
17
+ HW_FUNCTION = "hw".freeze
18
+ module_function
19
+ def path
20
+ File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "exe", "newline_hw"))
21
+ end
22
+
23
+ def cmd
24
+ <<-eos
25
+ function #{HW_FUNCTION}() {
26
+
27
+ setup_command=$(#{path} setup_command $*);
28
+ setup_es=$?;
29
+
30
+ if [ "$setup_es" = "0" ]; then
31
+ eval $setup_command;
32
+ setup_command_es=$?;
33
+
34
+ run_command=$(#{path} run_command $PWD $*);
35
+ run_es=$?;
36
+
37
+ if [ $run_es = "0" ] && [ $setup_command_es = "0" ]; then
38
+ eval $run_command;
39
+ else
40
+ echo "Could not run project run command";
41
+ echo $run_command;
42
+ fi
43
+ else
44
+ echo "Could not run project setup command";
45
+ echo $setup_command;
46
+ fi
47
+ }
48
+ eos
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,25 @@
1
+ require_relative "runners/base"
2
+ require_relative "runners/ruby"
3
+ require_relative "runners/javascript"
4
+
5
+ module NewlineHw
6
+ module Shell
7
+ ##
8
+ # Generate a series of language specfic commands to start a project up
9
+ class Run
10
+ attr_reader :pwd, :config
11
+ def initialize(pwd, config)
12
+ @pwd = pwd
13
+ @config = config
14
+ end
15
+
16
+ def cmd
17
+ commands = []
18
+ commands += Runners::Ruby.get_commands(pwd)
19
+ commands += Runners::Javascript.get_commands(pwd)
20
+ commands << "#{config.editor} ." if config.launch_editor
21
+ commands.join(" && ")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ module NewlineHw
2
+ module Shell
3
+ module Runners
4
+ class Base
5
+ attr_reader :commands, :pwd
6
+
7
+ def initialize(pwd)
8
+ @commands = []
9
+ @pwd = pwd
10
+ prepare_commands
11
+ end
12
+
13
+ def add_command(cmd)
14
+ @commands << cmd
15
+ end
16
+
17
+ def file_path(*args)
18
+ File.expand_path(File.join(pwd, *args))
19
+ end
20
+
21
+ def file?(*args)
22
+ File.exist?(file_path(*args))
23
+ end
24
+
25
+ def file_contents?(regex, *args)
26
+ return false unless file?(*args)
27
+ !File.readlines(file_path(*args)).grep(regex).empty?
28
+ end
29
+
30
+ def prepare_commands
31
+ raise NotImplementedError, "Subclases of Tiyo::Runners::Base must implement prepare_commands"
32
+ end
33
+
34
+ def self.get_commands(pwd)
35
+ new(pwd).commands
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ module NewlineHw
2
+ module Shell
3
+ module Runners
4
+ ##
5
+ # Build a shell command that is dependent on files of a project being
6
+ # present that are UNOPINIONATED about how to setup / start a JS project
7
+ class Javascript < Base
8
+ def npm?
9
+ file?("package.json")
10
+ end
11
+
12
+ def yarn?
13
+ file?("yarn.lock")
14
+ end
15
+
16
+ def prepare_commands
17
+ add_command "yarn" if yarn?
18
+ add_command "npm install" if npm? && !yarn?
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ module NewlineHw
2
+ module Shell
3
+ module Runners
4
+ ##
5
+ # Build a shell command that is dependent on files of a project being
6
+ # present that are UNOPINIONATED about how to setup / start a ruby related
7
+ # project
8
+ class Ruby < Base
9
+ def rails?
10
+ file?("bin", "rails")
11
+ end
12
+
13
+ def gemfile?
14
+ file?("Gemfile")
15
+ end
16
+
17
+ def spring?
18
+ file_contents?(/spring/, "Gemfile.lock")
19
+ end
20
+
21
+ def _rails_commands
22
+ add_command "spring stop" if spring?
23
+ add_command "bin/rake db:setup"
24
+ add_command "bin/rails s & sleep #{SLEEP_TIME} && open http://localhost:3000"
25
+ add_command "bin/rake test"
26
+ add_command "sleep 1 && %%" # Reown the rails s process
27
+ end
28
+
29
+ def prepare_commands
30
+ add_command "bundle install" if gemfile?
31
+ _rails_commands if rails?
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,110 @@
1
+ require "fileutils"
2
+ require "uri"
3
+ require "shellwords"
4
+
5
+ module NewlineHw
6
+ module Shell
7
+ ##
8
+ # Generate a series of language **AGNOISIC** commands to download and
9
+ # organize a students homework.
10
+ class Setup
11
+ BRANCH_NAME = "submitted_assignment".freeze
12
+ attr_accessor :url, :newline_submission_id, :config
13
+ def initialize(id_or_url, config)
14
+ @config = config
15
+ @newline_submission_id = Integer(id_or_url)
16
+ @url = submission_info["url"]
17
+ rescue ArgumentError
18
+ @config = config
19
+ @url = id_or_url
20
+ end
21
+
22
+ def submission_info
23
+ @_submission_info ||= query_submission_info
24
+ end
25
+
26
+ def cloneable?
27
+ pr? || gist? || github_project_link? || git? || false
28
+ end
29
+
30
+ def sha
31
+ return submission_info["sha"] if @newline_submission_id
32
+ matches = /\b[0-9a-f]{40}\b/.match(url)
33
+ matches.to_s if matches && !gist?
34
+ end
35
+
36
+ def git_url
37
+ return infer_git_url_from_pr if pr?
38
+ final_url = url.split("/tree/").first
39
+ "#{final_url}#{'.git' unless final_url.end_with?('.git')}"
40
+ end
41
+
42
+ def github_project_link?
43
+ url.starts_with?("https://github.com") && URI(url).path.split("/").reject{|l| l.empty?}.size == 2
44
+ end
45
+
46
+ def git?
47
+ url.starts_with?("git") || url.ends_with?(".git")
48
+ end
49
+
50
+ def gist?
51
+ /gist\.github\.com/.match(url)
52
+ end
53
+
54
+ def pr?
55
+ %r{\/\/github.com}.match(url) && %r{\/pull\/\d+}.match(url)
56
+ end
57
+
58
+ def dir_name
59
+ git_url.split(%r{[\/\.]})[-3..-2].join("-")
60
+ end
61
+
62
+ def clean_dir
63
+ FileUtils.rm_rf(File.join(homework_path, dir_name))
64
+ end
65
+
66
+ def homework_path
67
+ File.expand_path(config.homework_dir)
68
+ end
69
+
70
+ def setup
71
+ FileUtils.mkdir_p homework_path
72
+ end
73
+
74
+ def cmd
75
+ setup
76
+ clean_dir
77
+ cmds = []
78
+ cmds << "cd #{homework_path}"
79
+ cmds << "git clone #{git_url} #{dir_name}"
80
+ cmds << "cd #{dir_name}"
81
+ cmds << fetch_and_checkout_pr if pr?
82
+ cmds << "git checkout #{sha} -b #{BRANCH_NAME}" if sha
83
+ cmds << "echo #{Shellwords.escape JSON.pretty_generate submission_info} > .newline_submission_meta.json" if newline_submission_id
84
+ cmds.join(" && ")
85
+ end
86
+
87
+ private def fetch_and_checkout_pr
88
+ "git fetch origin pull/#{pr_id}/head:#{BRANCH_NAME} && git checkout #{BRANCH_NAME}"
89
+ end
90
+
91
+ private def infer_git_url_from_pr
92
+ url.split("/pull/").first + ".git"
93
+ end
94
+
95
+ private def pr_id
96
+ url.split("/pull/").last.to_i
97
+ end
98
+
99
+ private def query_submission_info
100
+ NewlineCli::Api.new.get("assignment_submissions/#{@newline_submission_id}")
101
+ rescue Excon::Error::Socket => e
102
+ puts "Error could not open a connection to newline #{e.message}"
103
+ exit 3
104
+ rescue Excon::Error::Forbidden => e
105
+ puts "You do not have access to this submission #{e.message}"
106
+ exit 3
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,82 @@
1
+ require "json"
2
+
3
+ module NewlineHw
4
+ ##
5
+ # A json message handler to trigger actions from chrome native messaging
6
+ class StreamCommandHandler
7
+ attr_reader :event, :data, :message_at
8
+ EVENTS = [
9
+ :heartbeat,
10
+ :clone_and_open_submission,
11
+ :check_if_cloneable
12
+ ].freeze
13
+
14
+ def initialize(event)
15
+ @event = event["event"].to_sym
16
+ @data = event["data"]
17
+ @message_at = event["message_at"].to_i
18
+ end
19
+
20
+ def known_event?
21
+ EVENTS.include?(event)
22
+ end
23
+
24
+ def call
25
+ return handle_unknown unless known_event?
26
+ send(event)
27
+
28
+ rescue StandardError => e
29
+ handle_fail(e)
30
+ end
31
+
32
+ def heartbeat
33
+ {
34
+ status: :ok,
35
+ message_at: message_at,
36
+ data: {
37
+ version: NewlineHw::VERSION,
38
+ newline_cli_version: NewlineCli::VERSION,
39
+ ruby_version: RUBY_VERSION,
40
+ config_path: Config::CONFIG_PATH
41
+ }
42
+ }
43
+ end
44
+
45
+ def check_if_cloneable
46
+ {
47
+ status: :ok,
48
+ message_at: message_at,
49
+ data: {
50
+ cloneable:
51
+ Shell::Setup.new(data["id"], Config.new).cloneable?
52
+ }
53
+ }
54
+ end
55
+
56
+ def clone_and_open_submission
57
+ {
58
+ status: :ok,
59
+ message_at: message_at,
60
+ data: GuiTrigger.new(data, Config.new).call
61
+ }
62
+ end
63
+
64
+ def handle_unknown
65
+ {
66
+ status: :fail,
67
+ message_at: message_at,
68
+ message: \
69
+ "no event handler found in Stream Command Handler for #{@event}"
70
+ }
71
+ end
72
+
73
+ def handle_fail(e)
74
+ {
75
+ status: :fail,
76
+ event: @event,
77
+ message_at: message_at,
78
+ message: e.message
79
+ }
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,42 @@
1
+ require "json"
2
+
3
+ module NewlineHw
4
+ class StreamProcessor
5
+ # Don't instant shutdown wait for messages to clear, as chrome is much faster
6
+ # to trigger disconnect callback over wait for succesful messages.
7
+ SHUTDOWN_PAUSE = 0.5
8
+ attr_reader :logger
9
+ def initialize(stdin, stdout, opts = {})
10
+ @logger = opts[:logger]
11
+ @stdin = stdin
12
+ @stdout = stdout
13
+ end
14
+
15
+ def on_message(&block)
16
+ loop do
17
+ msg = read_native_message
18
+ sleep(SHUTDOWN_PAUSE) && exit(0) unless msg
19
+ @logger.debug "Receiving Message #{msg} of size:#{msg.length}"
20
+ instance_exec(msg, &block)
21
+ end
22
+ end
23
+
24
+ def read_native_message
25
+ # Read signed integer with a max length of 4 bytes.
26
+ text_length_bytes = @stdin.read(4)
27
+ return unless text_length_bytes
28
+
29
+ # Unpack bytes in a ruby int.
30
+ text_length = text_length_bytes.unpack("i")[0]
31
+ JSON.parse(@stdin.read(text_length))
32
+ end
33
+
34
+ def send_message(message)
35
+ m = message.to_json
36
+ @logger.debug "Sending Message: #{m.length} #{m}"
37
+ @stdout.write [m.length].pack("I")
38
+ @stdout.write(m)
39
+ @stdout.flush
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module NewlineHw
2
+ VERSION = "1.0.0".freeze
3
+ end
data/lib/newline_hw.rb ADDED
@@ -0,0 +1,30 @@
1
+ require_relative "newline_hw/version"
2
+ require_relative "newline_hw/config"
3
+ require_relative "newline_hw/shell/run"
4
+ require_relative "newline_hw/shell/setup"
5
+ require_relative "newline_hw/shell/function"
6
+ require_relative "newline_hw/chrome_manifest"
7
+ require_relative "newline_hw/gui_trigger"
8
+ require_relative "newline_hw/stream_command_handler"
9
+ require_relative "newline_hw/stream_processor"
10
+ require "newline_cli"
11
+ require "active_support/core_ext/string"
12
+
13
+ module NewlineHw
14
+ SLEEP_TIME = 4
15
+
16
+ module_function
17
+
18
+ def config
19
+ Config.new
20
+ end
21
+
22
+ def logfile
23
+ File.expand_path config.log_file
24
+ end
25
+
26
+ def make_log_directory
27
+ return if Dir.exist?(File.dirname(NewlineHw.logfile))
28
+ Dir.mkdir(File.dirname(NewlineHw.logfile))
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "newline_hw/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "newline_hw"
8
+ spec.version = NewlineHw::VERSION
9
+ spec.authors = ["Russell Osborne"]
10
+ spec.email = ["russell@theironyard.com"]
11
+
12
+ spec.summary = "Quickly Clone and setup basic ruby and JS projects."
13
+ spec.description = "Quickly Clone and setup basic ruby and JS projects."
14
+ spec.homepage = "https://online.theironyard.com."
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+ spec.add_dependency "activesupport"
30
+ spec.add_dependency "json"
31
+ spec.add_dependency "thor"
32
+ spec.add_dependency "newline_cli"
33
+
34
+ spec.add_development_dependency "bundler", "~> 1.11"
35
+ spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "webmock", "~> 2.3"
37
+ end
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: newline_hw
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Russell Osborne
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-01-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: newline_cli
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.11'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.11'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.3'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.3'
111
+ description: Quickly Clone and setup basic ruby and JS projects.
112
+ email:
113
+ - russell@theironyard.com
114
+ executables:
115
+ - newline_hw
116
+ - newline_hw_stream
117
+ - newline_hw_stream_shim
118
+ extensions: []
119
+ extra_rdoc_files: []
120
+ files:
121
+ - ".gitignore"
122
+ - ".rspec"
123
+ - ".rubocop.yml"
124
+ - CODE_OF_CONDUCT.md
125
+ - Gemfile
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - bin/console
130
+ - bin/setup
131
+ - exe/newline_hw
132
+ - exe/newline_hw_stream
133
+ - exe/newline_hw_stream_shim
134
+ - lib/newline_hw.rb
135
+ - lib/newline_hw/chrome_manifest.rb
136
+ - lib/newline_hw/cli.rb
137
+ - lib/newline_hw/config.rb
138
+ - lib/newline_hw/gui_trigger.rb
139
+ - lib/newline_hw/shell/function.rb
140
+ - lib/newline_hw/shell/run.rb
141
+ - lib/newline_hw/shell/runners/base.rb
142
+ - lib/newline_hw/shell/runners/javascript.rb
143
+ - lib/newline_hw/shell/runners/ruby.rb
144
+ - lib/newline_hw/shell/setup.rb
145
+ - lib/newline_hw/stream_command_handler.rb
146
+ - lib/newline_hw/stream_processor.rb
147
+ - lib/newline_hw/version.rb
148
+ - newline_hw.gemspec
149
+ homepage: https://online.theironyard.com.
150
+ licenses:
151
+ - MIT
152
+ metadata:
153
+ allowed_push_host: https://rubygems.org/
154
+ post_install_message:
155
+ rdoc_options: []
156
+ require_paths:
157
+ - lib
158
+ required_ruby_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ requirements: []
169
+ rubyforge_project:
170
+ rubygems_version: 2.5.1
171
+ signing_key:
172
+ specification_version: 4
173
+ summary: Quickly Clone and setup basic ruby and JS projects.
174
+ test_files: []