caperoma 0.1.0 → 4.0.1
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 +5 -5
- data/.ruby-version +1 -0
- data/Capefile +48 -0
- data/Capefile.template +48 -0
- data/Capefile.test +20 -0
- data/Gemfile +25 -10
- data/Gemfile.lock +196 -77
- data/HELP +321 -0
- data/README.md +528 -0
- data/Rakefile +73 -18
- data/VERSION +1 -1
- data/bin/caperoma +47 -11
- data/caperoma.gemspec +144 -45
- data/config/crontab +10 -0
- data/config/schedule.rb +21 -0
- data/lib/caperoma.rb +409 -9
- data/lib/caperoma/models/account.rb +47 -0
- data/lib/caperoma/models/application_record.rb +5 -0
- data/lib/caperoma/models/branch.rb +6 -0
- data/lib/caperoma/models/project.rb +14 -0
- data/lib/caperoma/models/property.rb +5 -0
- data/lib/caperoma/models/report.rb +177 -0
- data/lib/caperoma/models/report_recipient.rb +6 -0
- data/lib/caperoma/models/reports/daily_report.rb +23 -0
- data/lib/caperoma/models/reports/retrospective_report.rb +19 -0
- data/lib/caperoma/models/reports/three_day_report.rb +19 -0
- data/lib/caperoma/models/task.rb +368 -0
- data/lib/caperoma/models/tasks/bug.rb +36 -0
- data/lib/caperoma/models/tasks/chore.rb +40 -0
- data/lib/caperoma/models/tasks/feature.rb +27 -0
- data/lib/caperoma/models/tasks/fix.rb +56 -0
- data/lib/caperoma/models/tasks/meeting.rb +40 -0
- data/lib/caperoma/models/tasks/modules/git.rb +65 -0
- data/lib/caperoma/models/tasks/task_with_commit.rb +40 -0
- data/lib/caperoma/models/tasks/task_with_separate_branch.rb +42 -0
- data/lib/caperoma/services/airbrake_email_processor.rb +47 -0
- data/lib/caperoma/services/pivotal_fetcher.rb +108 -0
- data/lib/caperoma/version.rb +9 -0
- data/spec/caperoma_spec.rb +3 -21
- data/spec/factories/accounts.rb +10 -0
- data/spec/factories/branches.rb +9 -0
- data/spec/factories/projects.rb +8 -0
- data/spec/factories/report_recipients.rb +7 -0
- data/spec/factories/reports.rb +16 -0
- data/spec/factories/tasks.rb +37 -0
- data/spec/features/bug_spec.rb +60 -0
- data/spec/features/chore_spec.rb +60 -0
- data/spec/features/command_unknown_spec.rb +14 -0
- data/spec/features/config_spec.rb +161 -0
- data/spec/features/feature_spec.rb +60 -0
- data/spec/features/finish_spec.rb +18 -0
- data/spec/features/fix_spec.rb +60 -0
- data/spec/features/meeting_spec.rb +22 -0
- data/spec/features/projects_spec.rb +17 -0
- data/spec/features/report_recipientss_spec.rb +117 -0
- data/spec/features/reports_spec.rb +65 -0
- data/spec/features/status_spec.rb +33 -0
- data/spec/features/version_spec.rb +11 -0
- data/spec/models/account_spec.rb +51 -0
- data/spec/models/branch_spec.rb +8 -0
- data/spec/models/bug_spec.rb +33 -0
- data/spec/models/chore_spec.rb +33 -0
- data/spec/models/daily_report_spec.rb +38 -0
- data/spec/models/feature_spec.rb +33 -0
- data/spec/models/fix_spec.rb +55 -0
- data/spec/models/meeting_spec.rb +33 -0
- data/spec/models/project_spec.rb +11 -0
- data/spec/models/report_recipient_spec.rb +22 -0
- data/spec/models/report_spec.rb +16 -0
- data/spec/models/retrospective_report_spec.rb +38 -0
- data/spec/models/task_spec.rb +613 -0
- data/spec/models/task_with_commit_spec.rb +105 -0
- data/spec/models/task_with_separate_branch_spec.rb +97 -0
- data/spec/models/three_day_report_spec.rb +49 -0
- data/spec/spec_helper.rb +26 -16
- data/spec/support/capefile_generator.rb +36 -0
- data/spec/support/database_cleaner.rb +21 -0
- data/spec/support/stubs.rb +178 -9
- metadata +283 -42
- data/.document +0 -5
- data/README.rdoc +0 -26
- data/lib/caperoma/credentials.rb +0 -13
- data/lib/caperoma/jira_client.rb +0 -57
- data/spec/caperoma/credentials_spec.rb +0 -25
- data/spec/caperoma/jira_spec.rb +0 -35
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Bug < TaskWithSeparateBranch
|
4
|
+
before_create :inform_creation_started
|
5
|
+
after_create :inform_creation_finished
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def create_issue_on_pivotal_data
|
10
|
+
Jbuilder.encode do |j|
|
11
|
+
j.current_state 'unstarted'
|
12
|
+
j.name title.to_s
|
13
|
+
j.story_type story_type
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def this_is_a_type_a_user_wants_to_create?
|
18
|
+
project.create_bugs_in_pivotal
|
19
|
+
end
|
20
|
+
|
21
|
+
def story_type
|
22
|
+
'bug'
|
23
|
+
end
|
24
|
+
|
25
|
+
def issue_type
|
26
|
+
project.bug_jira_task_id
|
27
|
+
end
|
28
|
+
|
29
|
+
def inform_creation_started
|
30
|
+
puts 'Starting a new bug'
|
31
|
+
end
|
32
|
+
|
33
|
+
def inform_creation_finished
|
34
|
+
puts 'A new bug started'
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class Chore < Task
|
3
|
+
before_create :inform_creation_started
|
4
|
+
after_create :inform_creation_finished
|
5
|
+
|
6
|
+
private
|
7
|
+
def create_issue_on_pivotal_data
|
8
|
+
Jbuilder.encode do |j|
|
9
|
+
j.current_state 'unstarted'
|
10
|
+
j.name title.to_s
|
11
|
+
j.story_type story_type
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def finish_on_pivotal_data
|
16
|
+
Jbuilder.encode do |j|
|
17
|
+
j.current_state 'accepted'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def this_is_a_type_a_user_wants_to_create?
|
22
|
+
project.create_chores_in_pivotal
|
23
|
+
end
|
24
|
+
|
25
|
+
def story_type
|
26
|
+
'chore'
|
27
|
+
end
|
28
|
+
|
29
|
+
def issue_type
|
30
|
+
project.chore_jira_task_id
|
31
|
+
end
|
32
|
+
|
33
|
+
def inform_creation_started
|
34
|
+
puts 'Starting a new chore'
|
35
|
+
end
|
36
|
+
|
37
|
+
def inform_creation_finished
|
38
|
+
puts 'A new chore started'
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class Feature < TaskWithSeparateBranch
|
3
|
+
before_create :inform_creation_started
|
4
|
+
after_create :inform_creation_finished
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def this_is_a_type_a_user_wants_to_create?
|
9
|
+
project.create_features_in_pivotal
|
10
|
+
end
|
11
|
+
|
12
|
+
def story_type
|
13
|
+
'feature'
|
14
|
+
end
|
15
|
+
|
16
|
+
def issue_type
|
17
|
+
project.feature_jira_task_id
|
18
|
+
end
|
19
|
+
|
20
|
+
def inform_creation_started
|
21
|
+
puts 'Starting a new feature'
|
22
|
+
end
|
23
|
+
|
24
|
+
def inform_creation_finished
|
25
|
+
puts 'A new feature started'
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class Fix < TaskWithCommit
|
3
|
+
before_create :update_parent_branch
|
4
|
+
before_create :inform_creation_started
|
5
|
+
after_create :inform_creation_finished
|
6
|
+
|
7
|
+
def description
|
8
|
+
result = super
|
9
|
+
last_commit = git_last_commit_name
|
10
|
+
"#{result}\n(For: #{last_commit})"
|
11
|
+
end
|
12
|
+
|
13
|
+
def finish(comment)
|
14
|
+
git_rebase_to_upstream
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def create_issue_on_pivotal_data
|
20
|
+
Jbuilder.encode do |j|
|
21
|
+
j.current_state 'unstarted'
|
22
|
+
j.name title.to_s
|
23
|
+
j.story_type story_type
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def finish_on_pivotal_data
|
28
|
+
Jbuilder.encode do |j|
|
29
|
+
j.current_state 'accepted'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def this_is_a_type_a_user_wants_to_create?
|
34
|
+
project.create_fixes_in_pivotal_as_chores
|
35
|
+
end
|
36
|
+
|
37
|
+
def story_type
|
38
|
+
'chore'
|
39
|
+
end
|
40
|
+
|
41
|
+
def update_parent_branch
|
42
|
+
git_rebase_to_upstream
|
43
|
+
end
|
44
|
+
|
45
|
+
def issue_type
|
46
|
+
project.fix_jira_task_id
|
47
|
+
end
|
48
|
+
|
49
|
+
def inform_creation_started
|
50
|
+
puts 'Starting a new fix'
|
51
|
+
end
|
52
|
+
|
53
|
+
def inform_creation_finished
|
54
|
+
puts 'A new fix started'
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class Meeting < Task
|
3
|
+
before_create :inform_creation_started
|
4
|
+
after_create :inform_creation_finished
|
5
|
+
|
6
|
+
private
|
7
|
+
def create_issue_on_pivotal_data
|
8
|
+
Jbuilder.encode do |j|
|
9
|
+
j.current_state 'unstarted'
|
10
|
+
j.name title.to_s
|
11
|
+
j.story_type story_type
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def finish_on_pivotal_data
|
16
|
+
Jbuilder.encode do |j|
|
17
|
+
j.current_state 'accepted'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def this_is_a_type_a_user_wants_to_create?
|
22
|
+
project.create_meetings_in_pivotal_as_chores
|
23
|
+
end
|
24
|
+
|
25
|
+
def story_type
|
26
|
+
'chore'
|
27
|
+
end
|
28
|
+
|
29
|
+
def issue_type
|
30
|
+
project.meeting_jira_task_id
|
31
|
+
end
|
32
|
+
|
33
|
+
def inform_creation_started
|
34
|
+
puts 'Starting a new meeting'
|
35
|
+
end
|
36
|
+
|
37
|
+
def inform_creation_finished
|
38
|
+
puts 'A new meeting started'
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Git
|
4
|
+
def git_branch(name)
|
5
|
+
`git -C "#{project.folder_path}" checkout -b #{name}` if ENV['CAPEROMA_INTEGRATION_TEST'].blank? && ENV['CAPEROMA_TEST'].blank?
|
6
|
+
end
|
7
|
+
|
8
|
+
def git_commit(msg)
|
9
|
+
`git -C "#{project.folder_path}" add -A && git -C "#{project.folder_path}" commit --allow-empty -m "#{msg}"` if ENV['CAPEROMA_INTEGRATION_TEST'].blank? && ENV['CAPEROMA_TEST'].blank?
|
10
|
+
end
|
11
|
+
|
12
|
+
def git_push
|
13
|
+
`git -C "#{project.folder_path}" push --set-upstream origin #{git_current_branch}` if ENV['CAPEROMA_INTEGRATION_TEST'].blank? && ENV['CAPEROMA_TEST'].blank?
|
14
|
+
end
|
15
|
+
|
16
|
+
def git_last_commit_name
|
17
|
+
`git -C "#{project.folder_path}" log --pretty=format:'%s' -1` if ENV['CAPEROMA_INTEGRATION_TEST'].blank? && ENV['CAPEROMA_TEST'].blank?
|
18
|
+
end
|
19
|
+
|
20
|
+
def git_current_branch
|
21
|
+
`git -C "#{project.folder_path}" rev-parse --abbrev-ref HEAD`.gsub("\n", '') if ENV['CAPEROMA_INTEGRATION_TEST'].blank? && ENV['CAPEROMA_TEST'].blank?
|
22
|
+
end
|
23
|
+
|
24
|
+
def git_pull_request(into, title, description = '')
|
25
|
+
pull_request_data = Jbuilder.encode do |j|
|
26
|
+
j.title title
|
27
|
+
j.body description
|
28
|
+
j.head git_current_branch
|
29
|
+
j.base into
|
30
|
+
end
|
31
|
+
|
32
|
+
conn = Faraday.new(url: 'https://api.github.com') do |c|
|
33
|
+
c.basic_auth(Account.git.email, Account.git.password)
|
34
|
+
c.adapter Faraday.default_adapter
|
35
|
+
end
|
36
|
+
|
37
|
+
conn.post do |request|
|
38
|
+
request.url "/repos/#{project.github_repo}/pulls"
|
39
|
+
request.body = pull_request_data
|
40
|
+
request.headers['User-Agent'] = 'Caperoma'
|
41
|
+
request.headers['Accept'] = 'application/vnd.github.v3+json'
|
42
|
+
request.headers['Content-Type'] = 'application/json'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def git_rebase_to_upstream
|
47
|
+
if ENV['CAPEROMA_INTEGRATION_TEST'].blank? && ENV['CAPEROMA_TEST'].blank?
|
48
|
+
has_untracked_files = !`git -C "#{project.folder_path}" ls-files --others --exclude-standard`.empty?
|
49
|
+
has_changes = !`git -C "#{project.folder_path}" diff`.empty?
|
50
|
+
has_staged_changes = !`git -C "#{project.folder_path}" diff HEAD`.empty?
|
51
|
+
|
52
|
+
changes_were_made = has_untracked_files || has_changes || has_staged_changes
|
53
|
+
|
54
|
+
`git -C "#{project.folder_path}" add -A && git -C "#{project.folder_path}" stash` if changes_were_made
|
55
|
+
|
56
|
+
`git -C "#{project.folder_path}" fetch && git -C "#{project.folder_path}" rebase $(git -C "#{project.folder_path}" rev-parse --abbrev-ref --symbolic-full-name @{u})`
|
57
|
+
|
58
|
+
`git -C "#{project.folder_path}" stash apply` if changes_were_made
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def git_checkout(branch)
|
63
|
+
`git -C "#{project.folder_path}" checkout #{branch}` if ENV['CAPEROMA_INTEGRATION_TEST'].blank? && ENV['CAPEROMA_TEST'].blank?
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class TaskWithCommit < Task
|
3
|
+
belongs_to :branch
|
4
|
+
|
5
|
+
def finish(comment)
|
6
|
+
super
|
7
|
+
git_commit(commit_message)
|
8
|
+
# here I should pass the path
|
9
|
+
`rubocop -a "#{project.folder_path}"` if ENV['CAPEROMA_INTEGRATION_TEST'].blank? && ENV['CAPEROMA_TEST'].blank?
|
10
|
+
git_commit(commit_rubocop_message)
|
11
|
+
git_push
|
12
|
+
end
|
13
|
+
|
14
|
+
def pause(comment)
|
15
|
+
super
|
16
|
+
git_commit(commit_message)
|
17
|
+
`rubocop -a "#{project.folder_path}"` if ENV['CAPEROMA_INTEGRATION_TEST'].blank? && ENV['CAPEROMA_TEST'].blank?
|
18
|
+
git_commit(commit_rubocop_message)
|
19
|
+
git_push
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def commit_message
|
25
|
+
# E.g.: [RUC-123][#1345231] Some Subject
|
26
|
+
string = ''
|
27
|
+
string += "[#{jira_key}]" if jira_key.present?
|
28
|
+
string += "[##{pivotal_id}]" if pivotal_id.present?
|
29
|
+
string += " #{title}"
|
30
|
+
string.strip
|
31
|
+
end
|
32
|
+
|
33
|
+
def commit_rubocop_message
|
34
|
+
string = ''
|
35
|
+
string += "[#{jira_key}]" if jira_key.present?
|
36
|
+
string += "[##{pivotal_id}]" if pivotal_id.present?
|
37
|
+
string += ' Applying good practices'
|
38
|
+
string.strip
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class TaskWithSeparateBranch < TaskWithCommit
|
3
|
+
before_create :update_parent_branch
|
4
|
+
before_create :remember_parent_branch
|
5
|
+
after_create :new_git_branch
|
6
|
+
|
7
|
+
def finish(comment)
|
8
|
+
puts comment
|
9
|
+
super
|
10
|
+
puts git_pull_request(parent_branch, title, description_for_pull_request)
|
11
|
+
puts git_checkout(parent_branch)
|
12
|
+
end
|
13
|
+
|
14
|
+
def abort(comment)
|
15
|
+
super
|
16
|
+
puts git_checkout(parent_branch)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def description_for_pull_request
|
22
|
+
pivotal_url
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_parent_branch
|
26
|
+
git_rebase_to_upstream
|
27
|
+
end
|
28
|
+
|
29
|
+
def remember_parent_branch
|
30
|
+
self.parent_branch = git_current_branch
|
31
|
+
end
|
32
|
+
|
33
|
+
def new_git_branch
|
34
|
+
git_branch(branch_name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def branch_name
|
38
|
+
# E.g.: ruc-123-first-three-four-words
|
39
|
+
result = [jira_key, title[0, 25]].join(' ')
|
40
|
+
ActiveSupport::Inflector.parameterize(result)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class AirbrakeEmailProcessor
|
4
|
+
# stub for test env
|
5
|
+
def process
|
6
|
+
account = Account.gmail
|
7
|
+
Gmail.new(account.email, account.password) do |gmail|
|
8
|
+
gmail.inbox.emails(:unread, from: 'donotreply@alerts.airbrake.io').select { |email| email.subject.include? '[Ruck.us]' }.each do |email|
|
9
|
+
# get reply address
|
10
|
+
reply_to = "#{email.to.first.mailbox}@#{email.to.first.host}"
|
11
|
+
|
12
|
+
# generate reply body
|
13
|
+
body = ''
|
14
|
+
|
15
|
+
story = PivotalFetcher.get_story_by_title(email.subject)
|
16
|
+
if story.present? && story.respond_to?(:url)
|
17
|
+
body = "Duplicate of: #{story.url}"
|
18
|
+
else
|
19
|
+
story = PivotalFetcher.create_story(email.subject, email.body.raw_source[0..1000])
|
20
|
+
body = "Created new story: #{story.url}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# compose reply email (to get identifiers)
|
24
|
+
reply = email.reply do
|
25
|
+
subject "Re: #{email.subject}"
|
26
|
+
body body
|
27
|
+
end
|
28
|
+
|
29
|
+
# bugfix to make reply work
|
30
|
+
new_email = gmail.compose do
|
31
|
+
to reply_to # note it's generated email, not airbrake one
|
32
|
+
subject reply.subject
|
33
|
+
in_reply_to reply.in_reply_to
|
34
|
+
references reply.references
|
35
|
+
body reply.body.raw_source
|
36
|
+
end
|
37
|
+
|
38
|
+
# deliver reply
|
39
|
+
new_email.deliver!
|
40
|
+
|
41
|
+
# archive email
|
42
|
+
email.mark(:read)
|
43
|
+
email.archive!
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PivotalFetcher
|
4
|
+
def self.process(story_id)
|
5
|
+
story = get_story(story_id)
|
6
|
+
|
7
|
+
if story.present?
|
8
|
+
title = story.name
|
9
|
+
description = story.description
|
10
|
+
type = story.story_type
|
11
|
+
# labels = story.labels # in case I do Critical thing
|
12
|
+
# something liek if lables.include?("hot") pass "critical" status to Jira.
|
13
|
+
|
14
|
+
story.update(current_state: 'started', owned_by: 'Serge Vinogradoff')
|
15
|
+
|
16
|
+
args = [type, title, description, story_id] # or args = [type, title, description, "1", story_id] ? I use that 1 in older versions
|
17
|
+
case type
|
18
|
+
when 'feature'
|
19
|
+
Caperoma.feature(args)
|
20
|
+
when 'bug'
|
21
|
+
Caperoma.bug(args)
|
22
|
+
else
|
23
|
+
puts 'Unknown story type in Pivotal'
|
24
|
+
end
|
25
|
+
|
26
|
+
# copy Jira ID to Pivotal story
|
27
|
+
task = Task.where(pivotal_id: story_id).first
|
28
|
+
if task.present?
|
29
|
+
task.jira_key
|
30
|
+
story.notes.create(text: task.jira_key)
|
31
|
+
else
|
32
|
+
puts 'task does not exist'
|
33
|
+
end
|
34
|
+
else
|
35
|
+
puts 'Did not find a story'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.finish(story_id)
|
40
|
+
story = nil
|
41
|
+
|
42
|
+
PivotalTracker::Project.all.each do |project|
|
43
|
+
story = project.stories.find(story_id)
|
44
|
+
break if story.present?
|
45
|
+
end
|
46
|
+
|
47
|
+
story
|
48
|
+
|
49
|
+
if story.present? && story.tasks.all.empty?
|
50
|
+
story.update current_state: 'finished'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.create_story(title, description)
|
55
|
+
connect
|
56
|
+
|
57
|
+
project_id = 993_892
|
58
|
+
|
59
|
+
# this isn't needed anymore... these are PT ID's of Ruck.us.
|
60
|
+
if title.include? '[Ruck.us] Production '
|
61
|
+
project_id = 993_892
|
62
|
+
elsif title.include? '[Ruck.us] Staging '
|
63
|
+
project_id = 1_110_744
|
64
|
+
elsif title.include? '[Ruck.us] Staging2 '
|
65
|
+
project_id = 1_266_704
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: icebox, need probably to move to backlog
|
69
|
+
project = PivotalTracker::Project.find(project_id)
|
70
|
+
story = project.stories.create name: title,
|
71
|
+
description: description,
|
72
|
+
requested_by: 'Serge Vinogradoff',
|
73
|
+
owned_by: 'Serge Vinogradoff',
|
74
|
+
story_type: 'bug'
|
75
|
+
|
76
|
+
story
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.get_story_by_title(title)
|
80
|
+
connect
|
81
|
+
|
82
|
+
story = nil
|
83
|
+
|
84
|
+
PivotalTracker::Project.all.each do |project|
|
85
|
+
story = project.stories.all.select { |x| x.name == title }.first
|
86
|
+
break if story.present?
|
87
|
+
end
|
88
|
+
|
89
|
+
story
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.get_story(story_id)
|
93
|
+
connect
|
94
|
+
|
95
|
+
story = nil
|
96
|
+
|
97
|
+
PivotalTracker::Project.all.each do |project|
|
98
|
+
story = project.stories.find(story_id)
|
99
|
+
break if story.present?
|
100
|
+
end
|
101
|
+
|
102
|
+
story
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.connect
|
106
|
+
PivotalTracker::Client.token(Account.pivotal.email, Account.pivotal.password)
|
107
|
+
end
|
108
|
+
end
|