Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
## Unreleased

### Compatible changes
* `geordi deploy` will now offer to move deployed issues to a new state if linear_team_ids are configured

### Breaking changes

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ Finds available Capistrano stages by their prefix, e.g. `geordi deploy p` will
deploy production, `geordi deploy mak` will deploy a `makandra` stage if there
is a file config/deploy/makandra.rb.

If Linear team ids are configured (see `geordi commit`'), will offer to move deployed issues to a new state.

When your project is running Capistrano 3, deployment will use `cap deploy`
instead of `cap deploy:migrations`. You can force using `deploy` by passing the
-M option: `geordi deploy -M staging`.
Expand Down
15 changes: 11 additions & 4 deletions features/deploy.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,31 @@ Feature: The deploy command
And a file named "config/deploy.rb" with "deploy.rb exists"
And a file named "config/deploy/staging.rb" with "staging.rb exists"


Scenario: Deploying from master to staging

Unfortunately, Aruba cannot run commands truly interactively. We need to
answer prompts blindly, and check the output afterwards.

Given the commit with the message "[W-367] Test commit" is going to be deployed
And a file named "tmp/local_settings.yml" with "linear_team_ids: abc23-def456-ghi67"

When I run `geordi deploy` interactively
# Answer three prompts
# Answer prompts
And I type "staging"
And I type "master"
And I type ""
And I type "target-branch"
And I type "Target Linear State"
# Confirm deployment
And I type "yes"
Then the output should contain:
"""
# You are about to:
> Merge branch master into target-branch
> Push these commits:
Util.run! git --no-pager log origin/target-branch..master --oneline
> Deploy to staging
> Move these Linear issues to state "Target Linear State":
[W-367] Test commit
Go ahead with the deployment? [n]
"""
And the output should contain:
Expand All @@ -32,7 +40,6 @@ Feature: The deploy command
> Deployment complete.
"""


Scenario: Deploying the current branch

Deploying the current branch requires support by the deployed application:
Expand Down
5 changes: 5 additions & 0 deletions features/step_definitions/miscellaneous_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
ENV['GEORDI_TESTING_ISSUE_MATCHES'] = 'true'
end

Given(/^the commit with the message "(.*)" is going to be deployed$/) do |commit_message|
ENV['GEORDI_TESTING_GIT_COMMIT'] = commit_message
end

After do
ENV['GEORDI_TESTING_STAGED_CHANGES'] = 'false'
ENV['GEORDI_TESTING_GIT_BRANCHES'] = nil
Expand All @@ -38,4 +42,5 @@
ENV['GEORDI_TESTING_RUBY_VERSION'] = nil
ENV['GEORDI_TESTING_DEFAULT_BRANCH'] = nil
ENV['GEORDI_TESTING_ISSUE_MATCHES'] = nil
ENV['GEORDI_TESTING_GIT_COMMIT'] = nil
end
18 changes: 16 additions & 2 deletions lib/geordi/commands/branch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,22 @@
option :from_master, aliases: %w[-m --from-main], type: :boolean, desc: 'Branch from master instead of the current branch'

def branch
require 'geordi/gitlinear'
Gitlinear.new.branch(from_master: options.from_master)
require 'geordi/linear_client'
require 'geordi/git'

issue = LinearClient.new.choose_issue

local_branches = Git.local_branch_names
matching_local_branch = local_branches.find { |branch_name| branch_name == issue['branchName'] }
matching_local_branch ||= local_branches.find { |branch_name| branch_name.include? issue['identifier'].to_s }

if matching_local_branch
Util.run! ['git', 'checkout', matching_local_branch]
else
default_branch = Git.default_branch
Util.run! ['git', 'checkout', default_branch] if options.from_master
Util.run! ['git', 'checkout', '-b', issue['branchName']]
end

Hint.did_you_know [
:commit,
Expand Down
19 changes: 17 additions & 2 deletions lib/geordi/commands/commit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,23 @@
LONGDESC

def commit(*git_args)
require 'geordi/gitlinear'
Gitlinear.new.commit(git_args)
require 'geordi/linear_client'
require 'geordi/git'
require 'highline'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check this: highline has recently been promoted to a runtime dependency. Which means, custom requires should not be necessary any more. Please check in geordi.gemspec, and if true, remove all "highline" requries (in a separate commit).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the case. While the entry in the gemspec causes gem install geordi to install highline, it does not make ruby require the gem at runtime.

This is a mechanism of bundler, which you need to explicitly call (require 'bundler/setup'; Bundler.require at the beginning of your code) and which is not supported for gemspec files, only for "proper" Gemfiles (because it would interfere with everything requiring geordi). Gems are expected to explicitly require all their dependencies without using bundler.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, thanks!

PS How did you see this comment :D


Interaction.warn <<~WARNING unless Git.staged_changes?
No staged changes. Will create an empty commit.
WARNING

linear_client = LinearClient.new
highline = HighLine.new

issue = linear_client.issue_from_branch || linear_client.choose_issue
title = "[#{issue['identifier']}] #{issue['title']}"
description = "Issue: #{issue['url']}"
extra = highline.ask("\nAdd an optional message").strip
title << ' - ' << extra if extra != ''
Util.run!(['git', 'commit', '--allow-empty', '-m', title, '-m', description, *git_args])

Hint.did_you_know [
:branch,
Expand Down
41 changes: 35 additions & 6 deletions lib/geordi/commands/deploy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
deploy production, `geordi deploy mak` will deploy a `makandra` stage if there
is a file config/deploy/makandra.rb.

If Linear team ids are configured (see `geordi commit`), will offer to move deployed issues to a new state.

When your project is running Capistrano 3, deployment will use `cap deploy`
instead of `cap deploy:migrations`. You can force using `deploy` by passing the
-M option: `geordi deploy -M staging`.
Expand All @@ -39,6 +41,12 @@
desc: 'Set DEPLOY_BRANCH to the current branch during deploy'

def deploy(target_stage = nil)
require 'geordi/git'
require 'geordi/linear_client'

settings = Settings.new
linear_client = LinearClient.new

# Set/Infer default values
branch_stage_map = { 'master' => 'staging', 'main' => 'staging', 'production' => 'production' }
if target_stage && !Util.deploy_targets.include?(target_stage)
Expand All @@ -48,7 +56,7 @@ def deploy(target_stage = nil)
end

# Ask for required information
target_stage ||= Interaction.prompt 'Deployment stage:', branch_stage_map.fetch(Util.current_branch, 'staging')
target_stage ||= Interaction.prompt 'Deployment stage:', branch_stage_map.fetch(Git.current_branch, 'staging')
capistrano_config = CapistranoConfig.new(target_stage)

if options.current_branch
Expand All @@ -60,18 +68,25 @@ def deploy(target_stage = nil)
set :branch, ENV['DEPLOY_BRANCH'] || 'master'
ERROR

source_branch = target_branch = Util.current_branch
source_branch = target_branch = Git.current_branch
else # Normal deploy
source_branch = Interaction.prompt 'Source branch:', Util.current_branch
source_branch = Interaction.prompt 'Source branch:', Git.current_branch

deploy_branch = capistrano_config.branch
deploy_branch ||= Util.git_default_branch
deploy_branch ||= Git.default_branch
target_branch = Interaction.prompt 'Deploy branch:', deploy_branch
end

if settings.linear_integration_set_up?
config_state = settings.linear_state_after_deploy(target_stage)
config_state = 'skip' if config_state.empty?
target_state = Interaction.prompt("Move deployed Linear issues to state:", config_state)
target_state = '' if target_state.empty? || target_state == 'skip'
settings.persist_linear_state_after_deploy(target_stage, target_state)
end

merge_needed = (source_branch != target_branch)
push_needed = merge_needed || `git cherry -v | wc -l`.strip.to_i > 0
push_needed = false if Util.testing? # Hard to test

Interaction.announce "Checking whether your #{source_branch} branch is ready" ############
Util.run!("git checkout #{source_branch}")
Expand All @@ -89,13 +104,23 @@ def deploy(target_stage = nil)

Interaction.announce 'You are about to:' #################################################
Interaction.note "Merge branch #{source_branch} into #{target_branch}" if merge_needed
linear_issue_ids = []
if push_needed
Interaction.note 'Push these commits:' if push_needed
Interaction.note 'Push these commits:'
Util.run!("git --no-pager log origin/#{target_branch}..#{source_branch} --oneline")

commit_messages = Git.commits_between(source_branch, target_branch)
linear_issue_ids = linear_client.extract_issue_ids(commit_messages)
end
Interaction.note "Deploy to #{target_stage}"
Interaction.note "From current branch #{source_branch}" if options.current_branch

if !linear_issue_ids.empty? && target_state && !target_state.empty?
relevant_commits = linear_client.filter_by_issue_ids(commit_messages, linear_issue_ids)
Interaction.note("Move these Linear issues to state \"#{target_state}\":")
puts relevant_commits.join("\n")
end

if Interaction.prompt('Go ahead with the deployment?', 'n', /y|yes/)
puts
git_call = []
Expand All @@ -115,6 +140,10 @@ def deploy(target_stage = nil)

Util.run!(capistrano_call, show_cmd: true)

if !linear_issue_ids.empty? && target_state && !target_state.empty?
linear_client.move_issues_to_state(linear_issue_ids, target_state)
end

Interaction.success 'Deployment complete.'

Hint.did_you_know [
Expand Down
4 changes: 3 additions & 1 deletion lib/geordi/commands/security_update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
LONGDESC

def security_update(step = 'prepare')
master = Util.git_default_branch
require 'geordi/git'

master = Git.default_branch

case step
when 'prepare'
Expand Down
53 changes: 53 additions & 0 deletions lib/geordi/git.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module Geordi
class Git
class << self
def local_branch_names
@local_branch_names ||= begin
branch_list_string = if Util.testing?
ENV['GEORDI_TESTING_GIT_BRANCHES'].to_s
else
`git branch --format="%(refname:short)"`
end

branch_list_string.strip.split("\n")
end
end

def current_branch
if Util.testing?
default_branch
else
`git rev-parse --abbrev-ref HEAD`.strip
end
end

def staged_changes?
if Util.testing?
ENV['GEORDI_TESTING_STAGED_CHANGES'] == 'true'
else
statuses = `git status --porcelain`.split("\n")
statuses.any? { |l| /^[A-Z]/i =~ l }
end
end

def default_branch
default_branch = if Util.testing?
ENV['GEORDI_TESTING_DEFAULT_BRANCH']
else
head_symref = `git ls-remote --symref origin HEAD`
head_symref[%r{\Aref: refs/heads/(\S+)\sHEAD}, 1]
end

default_branch || 'master'
end

def commits_between(source_branch, target_branch)
return [ENV['GEORDI_TESTING_GIT_COMMIT']] if Util.testing?

commits = `git --no-pager log --pretty=format:%s origin/#{target_branch}..#{source_branch}`

commits&.split("\n")
end
end
end
end
Loading