fa-harness-tools 1.0.5 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43514c409f804648259a4b51366d9d4eec846c54badb0642a9fb3d916d13ad8d
4
- data.tar.gz: 5c5e29f48aaf5e1f82a7cc608523086728e1085e89c9dde688c47db522b73b9f
3
+ metadata.gz: 421353286e74cdc70badf595a611fc8d9a34a5f14e4341cacac88bbba11cee19
4
+ data.tar.gz: f80a236d16ea035736191b65c2138abec2cff6c126d36b930b3866e01d50489d
5
5
  SHA512:
6
- metadata.gz: 070f23a423e9e104c294eb3c6fd47fb0d45d5edc904be12cef90e1c83cd22e372b97e7544f9f3824006066e1c3a285274a3c10e0d6b7b0badf5360acaf2dcc86
7
- data.tar.gz: 5cce7e38b1286181ae729433d5fdcd556dccb4c79500ee5b1c3cc471263ed767e714453ae10f910399954c4606c4ec1fbefbfec648e9f822a22ae151ba3b8ed8
6
+ metadata.gz: d610913dbd0979c1d6d5a19a04fe68b511042e333fbd4a616919855fb1d34dca130ea10e1e6a5d58d0579ad86fd796f9e2983a19ae222ddd2dea9b1015bbd229
7
+ data.tar.gz: 0fa44445386572aa266ef138f1bcc1bda3b84ea8b0fe6f98cfd86f17d58c0e5f045650c9b19bbc7309018f1cb9e0237f3e07f34f01e75b0110009d20a26035c3
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fa-harness-tools (1.0.5)
4
+ fa-harness-tools (1.1.0)
5
5
  octokit (~> 4.0)
6
+ pastel (~> 0.7)
6
7
  tzinfo (~> 2.0)
7
8
  tzinfo-data (~> 1.0)
8
9
 
@@ -13,12 +14,16 @@ GEM
13
14
  public_suffix (>= 2.0.2, < 5.0)
14
15
  concurrent-ruby (1.1.6)
15
16
  diff-lcs (1.3)
17
+ equatable (0.6.1)
16
18
  faraday (1.0.1)
17
19
  multipart-post (>= 1.2, < 3)
18
20
  multipart-post (2.1.1)
19
21
  octokit (4.18.0)
20
22
  faraday (>= 0.9)
21
23
  sawyer (~> 0.8.0, >= 0.5.3)
24
+ pastel (0.7.4)
25
+ equatable (~> 0.6)
26
+ tty-color (~> 0.5)
22
27
  public_suffix (4.0.5)
23
28
  rake (13.0.1)
24
29
  rspec (3.9.0)
@@ -38,6 +43,7 @@ GEM
38
43
  addressable (>= 2.3.5)
39
44
  faraday (> 0.8, < 2.0)
40
45
  timecop (0.9.1)
46
+ tty-color (0.5.1)
41
47
  tzinfo (2.0.2)
42
48
  concurrent-ruby (~> 1.0)
43
49
  tzinfo-data (1.2020.1)
data/README.md CHANGED
@@ -24,9 +24,9 @@ Examples below use [variables defined by Harness](https://docs.harness.io/articl
24
24
 
25
25
  Full scripts that can be used in Harness are available in the [examples/](examples/) directory.
26
26
 
27
- ### Required environment variables
27
+ ### Optional environment variables
28
28
 
29
- * `GITHUB_OAUTH_TOKEN` must be exported, containing a valid [personal access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) for GitHub
29
+ * To access private repositories, `GITHUB_OAUTH_TOKEN` must be exported, containing a valid [personal access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) for GitHub
30
30
 
31
31
  ### check-branch-brotection
32
32
 
@@ -9,11 +9,13 @@ export GITHUB_OAUTH_TOKEN="${secrets.getValue("github-oauth-token")}"
9
9
  run() {
10
10
  CMD=$1
11
11
  shift
12
+ echo
12
13
  $CMD \
13
14
  --build-no "${artifact.buildNo}" \
14
15
  --environment "${env.name}" \
15
16
  --repository "${artifact.source.repositoryName}" \
16
17
  "$@"
18
+ echo
17
19
  }
18
20
 
19
21
  # 1. Check we're within the daily deployment schedule
@@ -32,7 +32,7 @@ OptionParser.new do |opts|
32
32
  end.parse!
33
33
 
34
34
  client = FaHarnessTools::GithubClient.new(
35
- oauth_token: ENV.fetch("GITHUB_OAUTH_TOKEN"),
35
+ oauth_token: ENV.fetch("GITHUB_OAUTH_TOKEN", nil),
36
36
  owner: options.fetch(:github_owner),
37
37
  repo: options.fetch(:repo),
38
38
  )
@@ -48,10 +48,4 @@ result = FaHarnessTools::CheckBranchProtection.new(
48
48
  branch: options.fetch(:branch),
49
49
  ).verify?
50
50
 
51
- if result.first
52
- puts result.last
53
- exit 0
54
- else
55
- $stderr.puts result.last
56
- exit 1
57
- end
51
+ exit result ? 0 : 1
@@ -48,10 +48,4 @@ result = FaHarnessTools::CheckForwardDeploy.new(
48
48
  tag_prefix: options.fetch(:tag_prefix),
49
49
  ).verify?
50
50
 
51
- if result.first
52
- puts result.last
53
- exit 0
54
- else
55
- $stderr.puts result.last
56
- exit 1
57
- end
51
+ exit result ? 0 : 1
@@ -54,10 +54,4 @@ result = FaHarnessTools::CheckRecentDeploy.new(
54
54
  allowed_rollback_count: options.fetch(:allowed_rollback_count),
55
55
  ).verify?
56
56
 
57
- if result.first
58
- puts result.last
59
- exit 0
60
- else
61
- $stderr.puts result.last
62
- exit 1
63
- end
57
+ exit result ? 0 : 1
data/exe/check-schedule CHANGED
@@ -10,10 +10,4 @@ end.parse!
10
10
 
11
11
  result = FaHarnessTools::CheckSchedule.new.verify?
12
12
 
13
- if result.first
14
- puts result.last
15
- exit 0
16
- else
17
- $stderr.puts result.last
18
- exit 1
19
- end
13
+ exit result ? 0 : 1
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.require_paths = ["lib"]
28
28
 
29
29
  spec.add_runtime_dependency "octokit", "~> 4.0"
30
+ spec.add_runtime_dependency "pastel", "~> 0.7"
30
31
  spec.add_runtime_dependency "tzinfo", "~> 2.0"
31
32
  spec.add_runtime_dependency "tzinfo-data", "~> 1.0"
32
33
 
@@ -5,14 +5,23 @@ module FaHarnessTools
5
5
  @client = client
6
6
  @context = context
7
7
  @branch = branch
8
+ @logger = CheckLogger.new(
9
+ name: "Check branch protection",
10
+ description: "Only allow commits on the #{@branch} branch to be deployed",
11
+ )
8
12
  end
9
13
 
10
14
  def verify?
15
+ @logger.start
16
+ @logger.context_info(@client, @context)
17
+
11
18
  new_sha = @context.new_commit_sha
19
+
20
+ @logger.info("checking if #{@branch} branch contains the commit")
12
21
  if @client.branch_contains?(@branch, new_sha)
13
- [true, "#{@branch} contains #{new_sha}"]
22
+ @logger.pass "#{@branch} contains #{new_sha}"
14
23
  else
15
- [false, "#{@branch} does not contain #{new_sha}"]
24
+ @logger.fail "#{@branch} does not contain #{new_sha}"
16
25
  end
17
26
  end
18
27
  end
@@ -13,25 +13,36 @@ module FaHarnessTools
13
13
  @client = client
14
14
  @context = context
15
15
  @tag_prefix = tag_prefix
16
+ @logger = CheckLogger.new(
17
+ name: "Check forward deploy",
18
+ description: "Only allow deployments that are newer than what's currently deployed",
19
+ )
16
20
  end
17
21
 
18
22
  def verify?
23
+ @logger.start
24
+ @logger.context_info(@client, @context)
25
+
19
26
  current_tag = @client.last_deploy_tag(
20
27
  prefix: @tag_prefix, environment: @context.environment)
21
28
 
22
29
  if current_tag.nil?
23
30
  # If no previous deploys we need to let it deploy otherwise it will
24
31
  # never get past this check!
25
- return true, "first deploy"
32
+ @logger.info "no #{@tag_prefix} tag was found, so this must be the first deployment"
33
+ return @logger.pass("this is the first recorded deployment so is permitted")
26
34
  end
27
35
 
36
+ @logger.info("the most recent deployment is #{current_tag[:name]}")
37
+
28
38
  current_deployed_rev = current_tag[:commit][:sha]
29
39
  rev = @context.new_commit_sha
40
+ @logger.info("which means the currently deployed commit is #{current_deployed_rev}")
30
41
 
31
42
  if @client.is_ancestor_of?(current_deployed_rev, rev)
32
- [true, "forward deploy, #{rev} is ahead of #{current_deployed_rev}"]
43
+ @logger.pass "the commit being deployed is more recent than the currently deployed commit"
33
44
  else
34
- [false, "not a forward deploy, #{rev} is behind #{current_deployed_rev}"]
45
+ @logger.fail "the commit being deployed is before the currently deployed commit, so would revert changes"
35
46
  end
36
47
  end
37
48
  end
@@ -0,0 +1,34 @@
1
+ require "pastel"
2
+
3
+ module FaHarnessTools
4
+ class CheckLogger
5
+ def initialize(name:, description:)
6
+ @name = name
7
+ @description = description
8
+ @pastel = Pastel.new(enabled: true)
9
+ end
10
+
11
+ def start
12
+ puts @pastel.cyan(@pastel.bold(@name), %{ (#{@description})})
13
+ end
14
+
15
+ def info(message)
16
+ puts " ... #{message}"
17
+ end
18
+
19
+ def context_info(client, context)
20
+ info("we're deploying repo #{client.owner_repo} into environment #{context.environment}")
21
+ info("we're trying to deploy commit #{context.new_commit_sha}")
22
+ end
23
+
24
+ def pass(message)
25
+ puts @pastel.green("PASS: #{message}")
26
+ true
27
+ end
28
+
29
+ def fail(message)
30
+ puts @pastel.red("FAIL: #{message}")
31
+ false
32
+ end
33
+ end
34
+ end
@@ -27,9 +27,16 @@ module FaHarnessTools
27
27
  @context = context
28
28
  @tag_prefix = tag_prefix
29
29
  @allowed_rollback_count = allowed_rollback_count
30
+ @logger = CheckLogger.new(
31
+ name: "Check recent deploys",
32
+ description: "Only allow deployments of recent commits, up to #{@allowed_rollback_count} deployment rollbacks",
33
+ )
30
34
  end
31
35
 
32
36
  def verify?
37
+ @logger.start
38
+ @logger.context_info(@client, @context)
39
+
33
40
  tags = @client.
34
41
  all_deploy_tags(prefix: @tag_prefix, environment: @context.environment).
35
42
  sort_by { |tag| tag[:name] }
@@ -39,16 +46,20 @@ module FaHarnessTools
39
46
  if latest_allowed_tag.nil?
40
47
  # If no previous deploys we need to let it deploy otherwise it will
41
48
  # never get past this check!
42
- return true, "first deploy"
49
+ @logger.info "no #{@tag_prefix} tag was found, so this must be the first deployment"
50
+ return @logger.pass("this is the first recorded deployment so is permitted")
43
51
  end
44
52
 
53
+ @logger.info("the most recent tag allowed is #{latest_allowed_tag[:name]}")
54
+
45
55
  latest_allowed_rev = @client.get_commit_sha_from_tag(latest_allowed_tag)
46
56
  rev = @context.new_commit_sha
57
+ @logger.info("which means the most recent commit allowed is #{latest_allowed_rev}")
47
58
 
48
59
  if @client.is_ancestor_of?(latest_allowed_rev, rev)
49
- [true, "#{rev} is ahead of no.#{@allowed_rollback_count} most recent commit with #{@tag_prefix.inspect} tag"]
60
+ @logger.pass "the commit being deployed is more recent than the last permitted rollback commit"
50
61
  else
51
- [false, "#{rev} is prior to no.#{@allowed_rollback_count} most recent commit with #{@tag_prefix.inspect} tag"]
62
+ @logger.fail "the commit being deployed is older than the last permitted rollback commit"
52
63
  end
53
64
  end
54
65
  end
@@ -9,22 +9,33 @@ module FaHarnessTools
9
9
  class CheckSchedule
10
10
  def initialize(timezone: "Europe/London")
11
11
  tz = TZInfo::Timezone.get(timezone)
12
+ @timezone = timezone
12
13
  @now = tz.to_local(Time.now.utc)
14
+ @logger = CheckLogger.new(
15
+ name: "Check deployment schedule",
16
+ description: "Only allow deployments within certain times of the day",
17
+ )
13
18
  end
14
19
 
15
20
  def verify?
21
+ @logger.start
22
+ @logger.info("operating in the #{@timezone} timezone")
23
+ @logger.info("local time is #{@now}")
24
+
16
25
  permitted = false
17
26
  case @now.wday
18
27
  when 1..4
28
+ @logger.info("deployments are allowed between 9am to 4pm today (Mon-Thu)")
19
29
  permitted = true if @now.hour >= 9 && @now.hour < 16
20
30
  when 5
31
+ @logger.info("deployments are allowed between 9am to 12pm today (Fri)")
21
32
  permitted = true if @now.hour >= 9 && @now.hour < 12
22
33
  end
23
34
 
24
35
  if permitted
25
- [true, "scheduled deploy time"]
36
+ @logger.pass "inside the deployment schedule"
26
37
  else
27
- [false, "outside deployment schedule"]
38
+ @logger.fail "outside the deployment schedule"
28
39
  end
29
40
  end
30
41
  end
@@ -9,6 +9,8 @@ module FaHarnessTools
9
9
  @octokit = Octokit::Client.new(access_token: oauth_token)
10
10
  @owner = owner
11
11
  @repo = repo
12
+ @oauth_token = oauth_token
13
+ validate_repo
12
14
  end
13
15
 
14
16
  def owner_repo
@@ -91,14 +93,20 @@ module FaHarnessTools
91
93
  #
92
94
  # @return [Bool] True is <ancestor> is ancestor of <commit>
93
95
  def is_ancestor_of?(ancestor, commit)
94
- !!find_commit(commit) { |c| c[:sha] == ancestor }
96
+ # Compare returns the merge base, the common point in history between the
97
+ # two commits. If X is the ancestor of Y, then the merge base must be X.
98
+ # If not, it's a different branch.
99
+ @octokit.compare(owner_repo, ancestor, commit)[:merge_base_commit][:sha] == get_commit_sha(ancestor)
95
100
  end
96
101
 
97
102
  # Checks if <commit> is on branch <branch>
98
103
  #
99
104
  # @return [Bool] True is <commit> is on <branch>
100
105
  def branch_contains?(branch, commit)
101
- !!find_commit(branch) { |c| c[:sha] == commit }
106
+ # The same implementation works for this question. We have both methods
107
+ # to make the intent clearer and also this one is guaranteed to resolve a
108
+ # branch name for the first argument.
109
+ is_ancestor_of?(commit, branch)
102
110
  end
103
111
 
104
112
  # Creates a Git tag
@@ -112,16 +120,15 @@ module FaHarnessTools
112
120
 
113
121
  private
114
122
 
115
- # Paginate over commits from a given sha/branch, and exit early if the
116
- # supplied block matches
117
- def find_commit(sha_or_branch, &block)
118
- result = @octokit.commits(owner_repo, sha_or_branch).find(&block)
119
- response = @octokit.last_response
120
- until result || !response.rels[:next]
121
- response = response.rels[:next].get
122
- result = response.data.find(&block)
123
- end
124
- result
123
+ # Validates a repository exists.
124
+ # Raises a `LookupError` in the event a repository can't be found.
125
+ def validate_repo
126
+ @octokit.repo(owner_repo)
127
+
128
+ rescue Octokit::NotFound
129
+ message = "Unable to find repository #{owner_repo}"
130
+ message = "#{message}. If the repository is private, try setting GITHUB_OAUTH_TOKEN" unless @oauth_token
131
+ raise LookupError, message
125
132
  end
126
133
  end
127
134
  end
@@ -9,7 +9,7 @@ module FaHarnessTools
9
9
  end
10
10
 
11
11
  def new_commit_sha
12
- @client.get_commit_sha(@build_no)
12
+ @new_commit_sha ||= @client.get_commit_sha(@build_no)
13
13
  end
14
14
  end
15
15
  end
@@ -1,3 +1,3 @@
1
1
  module FaHarnessTools
2
- VERSION = "1.0.5"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -1,3 +1,4 @@
1
+ require "fa-harness-tools/check_logger"
1
2
  require "fa-harness-tools/check_branch_protection"
2
3
  require "fa-harness-tools/check_forward_deploy"
3
4
  require "fa-harness-tools/check_recent_deploy"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fa-harness-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - FreeAgent
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-20 00:00:00.000000000 Z
11
+ date: 2020-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: octokit
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pastel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.7'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: tzinfo
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -146,6 +160,7 @@ files:
146
160
  - lib/fa-harness-tools.rb
147
161
  - lib/fa-harness-tools/check_branch_protection.rb
148
162
  - lib/fa-harness-tools/check_forward_deploy.rb
163
+ - lib/fa-harness-tools/check_logger.rb
149
164
  - lib/fa-harness-tools/check_recent_deploy.rb
150
165
  - lib/fa-harness-tools/check_schedule.rb
151
166
  - lib/fa-harness-tools/github_client.rb