Commit b2bf2e2

mo khan <mo@mokhan.ca>
2025-02-10 21:47:47
Merge code from gem because the git history is lost
1 parent 5f6131b
exe/jive
@@ -3,4 +3,13 @@
 
 require "jive/cli"
 
-::Jive::Cli::App.start(ARGV)
+Signal.trap("INT") do
+  exit 1
+end
+
+begin
+  ::Jive::Cli::App.start(ARGV)
+rescue StandardError => e
+  puts "ERROR: #{e.message}"
+  exit 1
+end
lib/jive/templates/bug.md
@@ -0,0 +1,45 @@
+---
+title: "Defect Template"
+description: |
+---
+
+### Summary
+
+<!-- Summarize the defect encountered concisely. -->
+
+### Steps to reproduce
+
+<!--
+Describe how one can reproduce the issue - this is very important.
+Please use an ordered list.
+-->
+
+### Example Project
+
+<!--
+Please create an example project here on GitHub.com that exhibits the problematic
+behavior, and link to it here in the bug report.
+
+If you are using an older version of GitHub.com,
+this will also determine whether the bug is fixed
+in a more recent version.
+-->
+
+### What is the current *bug* behavior?
+
+<!-- Describe what actually happens. -->
+
+### What is the expected *correct* behavior?
+
+<!-- Describe what you should see instead. -->
+
+### Relevant logs and/or screenshots
+
+<!--
+Paste any relevant logs - please use code blocks (```) to format console output,
+logs, and code.
+-->
+
+### Possible fixes
+
+<!-- If you can, link to the line of code that might be responsible for the problem. -->
lib/jive/templates/feature.md
@@ -0,0 +1,49 @@
+---
+title: "Feature Template"
+description: |
+  This is used to break-up a large piece of work into smaller,
+  discrete tasks that can move independently through the build workflow steps.
+---
+
+As a `[persona]`, I `[want to]`, so that `[goal]`.
+
+# Why are we doing this work?
+
+<!--
+A brief explanation of the why, not the what or how.
+Assume the reader doesn't know the background and won't have time to dig-up
+information from comment threads.
+-->
+
+# Tasks
+
+<!--
+Steps and the parts of the code that will need to get updated.
+The plan can also call-out responsibilities for other team members or teams.
+
+e.g.:
+
+- [ ] ~frontend Step 1
+  - [ ] `@person` Step 1a
+- [ ] ~frontend Step 2
+- [ ] ~backend Step 3
+-->
+
+# Relevant links
+
+<!--
+Information that developers might need to refer to when implementing the issue.
+
+- [Design Issue](https://github.com/xlgmokha/project/issues/<id>)
+- [Similar implementation](https://github.com/xlgmokha/project/pull/<id>)
+-->
+
+# Non-functional requirements
+
+<!-- Add details for required items and delete others. -->
+
+- [ ] Documentation:
+- [ ] Feature flag:
+- [ ] Observability:
+- [ ] Performance:
+- [ ] Testing:
lib/jive/templates/play_book.md
@@ -0,0 +1,81 @@
+# Title
+
+<!--
+The title should be the name of the alert (e.g., Generic Alert_AlertTooGeneric).
+-->
+
+# Overview
+
+<!--
+Address the following:
+
+* What does this alert mean?
+* Is it a paging or an email-only alert?
+* What factors contributed to the alert?
+* What parts of the service are affected?
+* What other alerts accompany this alert?
+* Who should be notified?
+-->
+
+# Alert Severity
+
+<!--
+Indicate the reason for the severity (email or paging) of the alert and the
+impact of the alerted condition on the system or service.
+-->
+
+# Verification
+
+<!--
+Provide specific instructions on how to verify that the condition is ongoing.
+-->
+
+# Troubleshooting
+
+<!--
+
+List and describe debugging techniques and related information sources.
+Include links to relevant dashboards. Include warnings.
+
+Address the following:
+
+* What shows up in the logs when this alert fires?
+* What debug handlers are available?
+* What are some useful scripts or commands?
+* What sort of output do they generate?
+* What are some additional tasks that need to be done after the alert is resolved?
+
+-->
+
+# Solution
+
+<!--
+
+List and describe possible solutions for addressing this alert.
+
+Address the following:
+
+* How do I fix the problem and stop this alert?
+* What commands should be run to reset things?
+* Who should be contacted if this alert happened due to user behavior?
+* Who has expertise at debugging this issue?
+
+-->
+
+# Escalation
+
+<!--
+
+List and describe paths of escalation.
+Identify whom to notify (person or team) and when.
+If there is no need to escalate, indicate that.
+
+-->
+
+# Related Links
+
+<!--
+
+Provide links to relevant related alerts, procedures, and overview documentation.
+
+-->
lib/jive/templates/pull_request.md
@@ -0,0 +1,51 @@
+---
+title: "Pull Request Template"
+checklist:
+- [ ] Clear description explaining the relevancy of the contribution.
+- [ ] Unit, integration, and system tests.
+- [ ] Regression and bugs are covered with tests.
+- [ ] [Performance guidelines](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
+- [ ] [Secure coding guidelines](https://docs.gitlab.com/ee/development/secure_coding_guidelines.html)
+- [ ] Documentation Updated
+---
+
+# Why is this needed?
+
+## What does this do?
+
+<!--
+  Include a summary of the change and which issue is fixed.
+  Include relevant motivation and context.
+  List any dependencies that are required for this change.
+-->
+
+Fixes # (issue)
+
+### Screenshots
+
+Before:
+
+![Before][before]
+
+After:
+
+![After][after]
+
+## Type of change
+
+<!-- Delete options that are not relevant. -->
+
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
+- [ ] This change requires a documentation update
+
+## Verification Plan
+
+<!-- How are we going to verify this change? -->
+
+- [ ] Step 1
+- [ ] Step 2
+
+[after]: https://user-images.githubusercontent.com/x/y.png
+[before]: https://user-images.githubusercontent.com/x/y.png
lib/jive/cli.rb
@@ -1,5 +1,6 @@
 # frozen_string_literal: true
 
+require "cli/ui"
 require "pathname"
 require "thor"
 require "yaml"
@@ -52,6 +53,22 @@ module Jive
         end
       end)
 
+      desc "issue SUBCOMMAND ...ARGS", "issue things"
+      subcommand "issue", (Class.new(Thor) do
+        desc "list <type>", "List the issues"
+        def list(type = Issue.what_type?)
+          issues = Issue.for(type)
+          issue = Jive.prompt?(issues, display: ->(x) { x.file_name })
+          issue.edit
+        end
+
+        desc "create <type>", "Create a new issue"
+        def create(type = Issue.what_type?)
+          issue = Issue.create!(name: ask("Name:"), type: type)
+          issue.edit
+        end
+      end)
+
       desc "cd <org>/<project>", "cd to ~/src/github.com/<org>/<project>"
       def cd(slug)
         Jive.shell.run_safely { Git.new(Jive.shell).cd(slug) }
@@ -74,11 +91,9 @@ module Jive
           .bootstrap(Jive.shell)
       end
 
-      desc "pr URL", "pull request"
-      def pr(url = Repo.current.canonical_url)
-        return say("Invalid url") && exit(1) unless url
-
-        pr = PullRequest.new(url)
+      desc "pr", "pull request"
+      def pr
+        pr = PullRequest.new(repo: Repo.current)
         pr.edit(ENV["EDITOR"])
       end
 
lib/jive/git.rb
@@ -22,6 +22,7 @@ module Jive
       if dir.exist?
         shell.after_run([
           ["cd", dir],
+          ["ctags", dir],
           ["setenv", "JIVE_LAST_RUN=#{Time.now.to_i}"]
         ])
       else
lib/jive/issue.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+module Jive
+  class Issue
+    class << self
+      def what_type?
+        Jive.prompt?(
+          Jive.root
+            .join("lib/jive/templates")
+            .glob("*.md")
+            .map { |x| x.basename.to_s.gsub(".md", "") }
+        )
+      end
+
+      def create!(name:, type:, repo: Repo.current)
+        new(repo: repo, name: name, type: type)
+      end
+
+      def for(type, repo: Repo.current)
+        dir_for(type, repo: repo).glob("*.md").map do |x|
+          name = x.basename.to_s.gsub(".md", "")
+          new(repo: repo, name: name, type: type)
+        end
+      end
+
+      def dir_for(type, repo: Repo.current)
+        Jive.home
+            .join(repo.uri.host)
+            .join(repo.nwo)
+            .join(type)
+      end
+    end
+
+    def initialize(name:, type:, repo: Repo.current)
+      @repo = repo
+      @type = type
+      @name = name
+    end
+
+    def file_name
+      "#{name.gsub(/[^a-z0-9\-_]+/i, "-").downcase}.md"
+    end
+
+    def edit(editor: ENV["EDITOR"])
+      Jive.shell.execute([editor, issue.to_s])
+    end
+
+    private
+
+    attr_reader :repo, :name, :type
+
+    def issue
+      @issue ||=
+        begin
+          dir = self.class.dir_for(type, repo: repo)
+          Jive.shell.execute([:mkdir, "-p", dir])
+          dir.join(file_name).tap do |file|
+            file.write(template_for(type).read) unless file.exist?
+          end
+        end
+    end
+
+    def template_for(type)
+      Jive.root.join("lib/jive/templates/#{type}.md")
+    end
+  end
+end
lib/jive/pull_request.rb
@@ -4,9 +4,8 @@ module Jive
   class PullRequest
     attr_reader :dir, :uri
 
-    def initialize(url)
-      @uri = URI.parse(url)
-      @dir = Pathname(Dir.home).join(".jive").join(uri.host).join(uri.path[1..-1])
+    def initialize(repo: Repo.current)
+      @dir = Jive.home.join(repo.uri.host).join(repo.branch)
       Jive.shell.execute([:mkdir, "-p", @dir]) unless @dir.exist?
     end
 
@@ -17,7 +16,7 @@ module Jive
     private
 
     def template
-      Jive.root.join("lib/jive/templates/pull_request_template.md")
+      Jive.root.join("lib/jive/templates/pull_request.md")
     end
 
     def readme
lib/jive/repo.rb
@@ -8,6 +8,10 @@ module Jive
       @repo = Rugged::Repository.new(path.to_s)
     end
 
+    def uri
+      @uri ||= URI.parse(canonical_url)
+    end
+
     def canonical_url
       remote = @repo.remotes.find { |x| x.name == "origin" }
       return if remote.nil?
@@ -15,6 +19,15 @@ module Jive
       ssh?(remote) ? url_for_ssh(remote) : url_for(remote)
     end
 
+    def nwo
+      segments = uri.path.split("/")
+      "#{segments[1]}/#{segments[2].gsub(".git", "")}"
+    end
+
+    def branch
+      uri.path[1..-1]
+    end
+
     class << self
       def current
         @current ||= new(Pathname.pwd)
lib/jive/version.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 
 module Jive
-  VERSION = "0.4.4"
+  VERSION = "0.7.0"
 end
lib/jive.rb
@@ -8,6 +8,7 @@ require "uri"
 require "jive/batch_runner"
 require "jive/docker"
 require "jive/git"
+require "jive/issue"
 require "jive/popen"
 require "jive/project"
 require "jive/pull_request"
@@ -30,4 +31,18 @@ module Jive
   def self.shell
     @shell ||= ::Jive::Shell.new
   end
+
+  def self.home
+    @home ||= Pathname(Dir.home).join(".jive")
+  end
+
+  def self.prompt?(items, display: ->(x) { x })
+    CLI::UI::Prompt.ask("Choose?") do |handler|
+      items.each do |item|
+        handler.option(display.call(item)) do |_selection|
+          return item
+        end
+      end
+    end
+  end
 end
jive.gemspec
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
       $ jive setup
   MESSAGE
 
+  spec.add_dependency "cli-ui", "~> 1.5"
   spec.add_dependency "rugged", "~> 1.1"
   spec.add_dependency "thor", "~> 1.1"
   spec.add_development_dependency "minitest", "~> 5.0"
jive.sh
@@ -56,6 +56,10 @@ __jive_execute_task() {
       # shellcheck disable=SC2164
       cd "${task//cd:/}"
       ;;
+    ctags:*)
+      # shellcheck disable=SC2164
+      ctags -R "${task//ctags:/}"
+      ;;
     setenv:*)
       export "${task//setenv:/}"
       ;;