full360-sequencer 0.1.3 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54bca4472a18632b312e84aff4c3ca5958053645dca8860f579c27e257689337
4
- data.tar.gz: 024a6bad17c794fe021cfd31df13a03ada093aa4c0c12c3dfd7432e38b43a518
3
+ metadata.gz: 55cfadcc1b8ded41f44640d6a9192da0516e666ed7945b952be654a5b3c67b47
4
+ data.tar.gz: 3203c2e07fce0679b2135b5d9fda842680b39217b17936c3de09c86a72407ba3
5
5
  SHA512:
6
- metadata.gz: 04e30b01ccbb05f4622d1596c37cbff99ad15cabd7ab8e6bd8bc1906a46482a35e7f1cc5fc51588e66b281a71ea3e31b4ab945b01abe5650cbf30eb777117c16
7
- data.tar.gz: 510b7447b7182e6a746d6584a48ea4949aeeeb320a222bc0605e4ebcae93729f56928dd301d2ae3fa01a54843364a9f5672c1f2526197374b4425f8a0fd57015
6
+ metadata.gz: 2cda7c63a37691c7c0a5f818a4a462b9dbd36926f7b601730d31c0601ad438ece6a1e0b29ea17092ac809f0c928676c092a135a9d770c71278610bdfcb7d9b14
7
+ data.tar.gz: dfd985f05dadc66ad451420f2677fedc0c28ab8c83b59cc4842ee69714e2e40469499be79238e9b826fb09a9d8141854298f0fd2a6d793421d0c491362519d4d
@@ -1,17 +1,14 @@
1
1
  name: Release Gem
2
2
 
3
3
  on:
4
- workflow_run:
5
- workflows: ["CI Test"]
6
- types: [completed]
7
- branches:
8
- - master
4
+ push:
9
5
  tags:
10
- - v*
6
+ - "v*"
11
7
 
12
8
  jobs:
13
9
  release:
14
10
  runs-on: ubuntu-latest
11
+
15
12
  steps:
16
13
  # Checkout code if release was created
17
14
  - uses: actions/checkout@v2
@@ -33,3 +30,65 @@ jobs:
33
30
  gem push *.gem
34
31
  env:
35
32
  RUBYGEMS_API_KEY: "${{secrets.RUBYGEMS_API_KEY}}"
33
+
34
+ docker-jruby:
35
+ runs-on: ubuntu-latest
36
+ needs: release
37
+
38
+ steps:
39
+ - name: Checkout
40
+ uses: actions/checkout@v2
41
+
42
+ - name: Set up Docker Buildx
43
+ uses: docker/setup-buildx-action@v1
44
+
45
+ - name: Login to DockerHub
46
+ uses: docker/login-action@v1
47
+ with:
48
+ username: ${{ secrets.DOCKER_HUB_USERNAME }}
49
+ password: ${{ secrets.DOCKER_HUB_TOKEN }}
50
+
51
+ - name: Extract tag name
52
+ id: tag
53
+ run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
54
+
55
+ - name: Build JRuby image
56
+ uses: docker/build-push-action@v2
57
+ with:
58
+ file: ./Dockerfile.jruby
59
+ pull: true
60
+ push: true
61
+ tags: |
62
+ full360/sequencer:latest-jruby
63
+ full360/sequencer:${{ steps.tag.outputs.VERSION }}-jruby
64
+
65
+ docker-ruby:
66
+ runs-on: ubuntu-latest
67
+ needs: release
68
+
69
+ steps:
70
+ - name: Checkout
71
+ uses: actions/checkout@v2
72
+
73
+ - name: Set up Docker Buildx
74
+ uses: docker/setup-buildx-action@v1
75
+
76
+ - name: Login to DockerHub
77
+ uses: docker/login-action@v1
78
+ with:
79
+ username: ${{ secrets.DOCKER_HUB_USERNAME }}
80
+ password: ${{ secrets.DOCKER_HUB_TOKEN }}
81
+
82
+ - name: Extract tag name
83
+ id: tag
84
+ run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
85
+
86
+ - name: Build Ruby image
87
+ uses: docker/build-push-action@v2
88
+ with:
89
+ file: ./Dockerfile.ruby
90
+ pull: true
91
+ push: true
92
+ tags: |
93
+ full360/sequencer:latest-jruby
94
+ full360/sequencer:${{ steps.tag.outputs.VERSION }}-ruby
@@ -9,7 +9,7 @@ jobs:
9
9
  matrix:
10
10
  os: [ubuntu-latest, macos-latest]
11
11
  # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
12
- ruby: ["2.7", "3.0", "jruby-9.2.19.0", "jruby-9.3.0.0"]
12
+ ruby: ["2.7", "jruby-9.2.19.0", "jruby-9.3.0.0"]
13
13
  runs-on: ${{ matrix.os }}
14
14
 
15
15
  if: "!contains(github.event.head_commit.message, '[ci skip]')"
data/CHANGELOG.md CHANGED
@@ -10,6 +10,47 @@ Versioning](http://semver.org/spec/v2.0.0.html).
10
10
  ### Changed
11
11
  ### Removed
12
12
 
13
+ ## 0.2.7
14
+ ### Added
15
+ ### Changed
16
+ - Normalize the Docker image tags to `latest-jruby/ruby` and `vX.X.X-jruby/ruby`
17
+ ### Removed
18
+
19
+ ## 0.2.6
20
+ ### Added
21
+ ### Changed
22
+ ### Removed
23
+ - Remove the `github-script` action.
24
+
25
+ ## 0.2.5
26
+ ### Added
27
+ ### Changed
28
+ - Multiple workflow changese to make it work.
29
+ ### Removed
30
+
31
+ ## 0.2.0
32
+ ### Added
33
+ - Add Docker image for Ruby and JRuby that will get triggered after the release.
34
+ ### Changed
35
+ - Refactor the code to make it easier to read and modify.
36
+ - Update the AWS SDK for ECS to V3.
37
+ - Fix a missing logger dependency in the bin/sequencer code.
38
+ ### Removed
39
+ - Tests for Ruby 3.0 as they were failing for an unknown reason and I couldn't
40
+ reproduce locally.
41
+
42
+ ## 0.1.3
43
+ ### Added
44
+ - Fix release workflow.
45
+ ### Changed
46
+ ### Removed
47
+
48
+ ## 0.1.2
49
+ ### Added
50
+ - Fix release workflow.
51
+ ### Changed
52
+ ### Removed
53
+
13
54
  ## 0.1.1
14
55
  ### Added
15
56
  - Workflows dependable.
data/Dockerfile.jruby ADDED
@@ -0,0 +1,23 @@
1
+ FROM jruby:9.2-jre8
2
+
3
+ # Should be more than sufficient for transforms without large recordset
4
+ # operations.
5
+ ENV JRUBY_OPTS=-J-Xmx1024m
6
+
7
+ RUN apt-get update \
8
+ && apt-get install -y --no-install-recommends \
9
+ unzip \
10
+ ca-certificates \
11
+ awscli \
12
+ && update-ca-certificates \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ #set time zone
16
+ RUN mv /etc/localtime /etc/localtime.bak ; ln -s /usr/share/zoneinfo/UTC /etc/localtime
17
+
18
+ RUN gem install full360-sequencer
19
+
20
+ # this is the entry point
21
+ ADD entrypoint.sh /usr/sbin/runner.sh
22
+ RUN chmod 755 /usr/sbin/runner.sh
23
+ ENTRYPOINT ["/usr/sbin/runner.sh"]
data/Dockerfile.ruby ADDED
@@ -0,0 +1,21 @@
1
+ FROM ruby:2.7-alpine
2
+
3
+ # Install required packages
4
+ RUN apk add --no-cache \
5
+ unzip \
6
+ ca-certificates \
7
+ bash \
8
+ wget \
9
+ curl \
10
+ aws-cli \
11
+ && update-ca-certificates \
12
+
13
+ #set time zone
14
+ RUN mv /etc/localtime /etc/localtime.bak ; ln -s /usr/share/zoneinfo/UTC /etc/localtime
15
+
16
+ RUN gem install full360-sequencer
17
+
18
+ # this is the entry point
19
+ ADD entrypoint.sh /usr/sbin/runner.sh
20
+ RUN chmod 755 /usr/sbin/runner.sh
21
+ ENTRYPOINT ["/usr/sbin/runner.sh"]
data/Gemfile.lock CHANGED
@@ -1,20 +1,22 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- full360-sequencer (0.1.3)
5
- aws-sdk (~> 2.9)
4
+ full360-sequencer (0.2.7)
5
+ aws-sdk-ecs (~> 1.85)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  aws-eventstream (1.2.0)
11
- aws-sdk (2.11.632)
12
- aws-sdk-resources (= 2.11.632)
13
- aws-sdk-core (2.11.632)
14
- aws-sigv4 (~> 1.0)
11
+ aws-partitions (1.510.0)
12
+ aws-sdk-core (3.121.1)
13
+ aws-eventstream (~> 1, >= 1.0.2)
14
+ aws-partitions (~> 1, >= 1.239.0)
15
+ aws-sigv4 (~> 1.1)
15
16
  jmespath (~> 1.0)
16
- aws-sdk-resources (2.11.632)
17
- aws-sdk-core (= 2.11.632)
17
+ aws-sdk-ecs (1.85.0)
18
+ aws-sdk-core (~> 3, >= 3.120.0)
19
+ aws-sigv4 (~> 1.1)
18
20
  aws-sigv4 (1.4.0)
19
21
  aws-eventstream (~> 1, >= 1.0.2)
20
22
  coderay (1.1.3)
@@ -47,4 +49,4 @@ DEPENDENCIES
47
49
  rake (~> 12)
48
50
 
49
51
  BUNDLED WITH
50
- 2.2.27
52
+ 2.2.28
data/README.md CHANGED
@@ -94,4 +94,11 @@ Releasing a new version of the Gem requires a few steps:
94
94
  - Create a Git tag that matches the version number in `version.rb`
95
95
  - Example: `git tag -m "Version 1.0.0" v1.0.0`
96
96
 
97
+ ## Running the Sequencer from Docker
98
+
99
+ Pass the following environment variables to the container at runtime:
100
+
101
+ * `SEQUENCER_YAML_S3_PATH` - s3:// path to the yaml configuration file.
102
+ * `AWS_REGION` - AWS region in which you are running the container.
103
+
97
104
  [ref]: http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_RunTask.html
data/bin/sequencer CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require "logger"
3
4
  require "full360-sequencer"
4
5
 
5
6
  config_file = ARGV[0]
@@ -14,7 +15,7 @@ def logger
14
15
  end
15
16
 
16
17
  def sequencer_version
17
- Gem.loaded_specs["full360-sequencer"].version
18
+ Full360::Sequencer::VERSION
18
19
  end
19
20
 
20
21
  begin
@@ -22,20 +23,20 @@ begin
22
23
  logger.level = ENV["SEQUENCER_LOG_DEBUG"] ? Logger::DEBUG : Logger::INFO
23
24
 
24
25
  if config_file == nil
25
- logger.error("SEQUENCER_ERROR")
26
- logger.error("YAML file not provided... exiting with error code 1")
26
+ logger.error("SEQUENCER_ERROR: YAML file not provided... exiting with error code 1")
27
27
  exit 1
28
28
  else
29
- r = Full360::Sequencer::Runner.new(logger)
30
- r.config_from_file(config_file)
31
- r.sleep_between_checks = ENV["SEQUENCER_SLEEP_BETWEEN_CHECKS"].to_i if ENV["SEQUENCER_SLEEP_BETWEEN_CHECKS"]
32
- r.run
29
+ sleep_between_checks = ENV.fetch("SEQUENCER_SLEEP_BETWEEN_CHECKS", 5).to_i
30
+
31
+ Full360::Sequencer::Runner.new(sleep_between_checks, logger).tap do |runner|
32
+ runner.config_from_file(config_file)
33
+ runner.run
34
+ end
33
35
  end
34
36
  logger.info("all steps succeeded... exiting with code 0")
35
37
  exit 0
36
38
  rescue => e
37
- logger.error("SEQUENCER_ERROR")
38
- logger.error(e.message)
39
+ logger.error("SEQUENCER_ERROR: #{e.message}")
39
40
  e.backtrace.each { |r| logger.error(r) }
40
41
  logger.error("failure... exiting with code 1")
41
42
  exit 1
data/entrypoint.sh ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+
3
+ if [ -n "$AWS_REGION" ]
4
+ then
5
+ aws s3 cp $SEQUENCER_YAML_S3_PATH /sequencer/file.yml --region $AWS_REGION
6
+ else
7
+ aws s3 cp $SEQUENCER_YAML_S3_PATH /sequencer/file.yml
8
+ fi
9
+
10
+ sequencer /sequencer/file.yml
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  # Make it Ruby 2+ only
26
26
  spec.required_ruby_version = ">= 2.0"
27
27
 
28
- spec.add_runtime_dependency "aws-sdk", "~> 2.9"
28
+ spec.add_runtime_dependency "aws-sdk-ecs", "~> 1.85"
29
29
 
30
30
  # development dependencies
31
31
  spec.add_development_dependency "minitest", "~> 5.9"
@@ -1,95 +1,101 @@
1
- require "aws-sdk"
2
1
  require "logger"
2
+ require "aws-sdk-ecs"
3
3
 
4
4
  module Full360
5
5
  module Sequencer
6
6
  class RunECSTask < RunTaskBase
7
- def initialize(task_name, params, logger = nil)
8
- @logger = logger ? logger : Logger.new(STDOUT)
9
- @task_name = task_name
10
- @params = params["parameters"]
11
- @params = keys_to_symbol(@params)
12
- @cluster = @params[:cluster]
13
- end
7
+ attr_accessor :task_name
8
+ attr_accessor :params
9
+ attr_accessor :ecs_client
10
+ attr_accessor :logger
14
11
 
15
- def keys_to_symbol(params)
16
- # replaces string keys with symbol keys
17
- # required by AWS SDK
18
- if params.is_a?(Hash)
19
- params.inject({}){ |memo,(k,v)| memo[k.to_sym] = v; memo }
20
- else
21
- nil
22
- end
12
+ attr_reader :cluster
13
+ attr_reader :task_arn
14
+ attr_reader :start_time
15
+
16
+ def initialize(task_name, params, ecs_client = nil, logger = nil)
17
+ @logger = logger ||= Logger.new(STDOUT)
18
+ @ecs_client = ecs_client ||= Aws::ECS::Client.new
19
+ @task_name = task_name
20
+ @params = params[:parameters]
21
+ @cluster = self.params[:cluster]
23
22
  end
24
23
 
25
24
  def run_task
26
- @logger.info("starting ECS task #{@task_name}")
25
+ logger.info("starting ECS task #{task_name}")
26
+
27
27
  resp = ecs_run_task
28
- @task_arn = resp.tasks[0].task_arn
29
- @logger.info("#{@task_name} task created #{@task_arn} on cluster #{@cluster}")
28
+ @task_arn = resp.tasks.first.task_arn
29
+
30
+ logger.info("#{task_name} task created #{task_arn} on cluster #{cluster}")
30
31
  end
31
32
 
32
33
  def ecs_run_task
33
- @logger.debug("creating AWS client for ECS task #{@task_name}...")
34
- @ecs_client = ::Aws::ECS::Client.new
35
- @logger.debug("running ECS task #{@task_name}...")
36
- @start_time = Time.new
37
- resp = @ecs_client.run_task(@params)
38
- return resp
34
+ logger.debug("running ECS task #{task_name}...")
35
+ @start_time = Time.new.utc
36
+
37
+ resp = ecs_client.run_task(params)
38
+ resp
39
39
  rescue => e
40
- @logger.error("SEQUENCER_ERROR")
41
- @logger.error("error creating ECS task...")
42
- @logger.error("response from ECS: #{resp}")
40
+ logger.error("SEQUENCER_ERROR: response from ECS #{resp}")
43
41
  raise e
44
42
  end
45
43
 
46
44
  def ecs_describe_tasks
47
- @ecs_client.describe_tasks(
45
+ ecs_client.describe_tasks(
48
46
  {
49
- cluster: @cluster,
50
- tasks: [@task_arn] # required
47
+ cluster: cluster,
48
+ tasks: [task_arn],
51
49
  }
52
50
  )
53
51
  end
54
52
 
55
53
  def completed?
56
54
  retries ||= 0
55
+
57
56
  resp = ecs_describe_tasks
58
57
  status = last_task_status(resp)
59
- @logger.info("#{@task_name} : #{@task_arn} current status: #{status}")
58
+
59
+ logger.info("#{task_name}: #{task_arn} current status: #{status}")
60
+
61
+ completed = false
62
+
60
63
  if status == "STOPPED"
61
- @logger.info("#{@task_name} completed in #{Time.new - @start_time} seconds")
64
+ logger.info("#{task_name} completed in #{Time.new.utc - start_time} seconds")
62
65
  # parse exit_code(s) and return completion
63
66
  @success = determine_success(resp)
64
- return true
67
+ completed = true
65
68
  end
66
- false
69
+
70
+ completed
67
71
  rescue => e
68
- @logger.warn(e.message)
69
- @logger.warn("task completion check failed, trying again ##{ retries }")
72
+ logger.warn(e.message)
73
+ logger.warn("task completion check failed, trying again ##{retries}")
74
+
70
75
  sleep 10*retries
76
+
71
77
  retry if (retries += 1) < 3
72
78
 
73
- @logger.error("SEQUENCER_ERROR")
74
- @logger.error(e.message)
75
- e.backtrace.each { |r| @logger.error(r) }
79
+ logger.error("SEQUENCER_ERROR: #{e.message}")
80
+ e.backtrace.each { |r| logger.error(r) }
76
81
  end
77
82
 
78
83
  # parses last status from aws API response
79
84
  def last_task_status(resp)
80
- resp.tasks[0].last_status
85
+ resp.tasks.first.last_status
81
86
  end
82
87
 
83
88
  # success is determined by all containers having zero exit code
84
89
  def determine_success(resp)
85
90
  success = true
86
- resp.tasks[0].containers.each do |c|
87
- @logger.info("#{@task_name} : container #{c.name} #{c.container_arn} completed with exit_code #{c.exit_code}")
88
- if c.exit_code != 0
89
- # we had a problem!
90
- success = false
91
- end
91
+
92
+ resp.tasks.first.containers.each do |c|
93
+ logger.info("#{task_name}: container #{c.name} #{c.container_arn} completed with exit_code #{c.exit_code}")
94
+
95
+ # we had a problem!
96
+ success = false if c.exit_code != 0
92
97
  end
98
+
93
99
  success
94
100
  end
95
101
  end
@@ -1,17 +1,22 @@
1
1
  require "yaml"
2
2
  require "logger"
3
+ require "full360_sequencer/run_ecs_task"
3
4
 
4
5
  module Full360
5
6
  module Sequencer
6
7
  class Runner
7
8
  attr_accessor :sleep_between_checks
8
- attr_accessor :config
9
+ attr_accessor :logger
9
10
 
10
- def initialize(logger = nil)
11
- @logger = logger ? logger : Logger.new(STDOUT)
11
+ attr_reader :config
12
12
 
13
- # default 5 seconds between completed? checks
14
- @sleep_between_checks = 5
13
+ # Initializes the class
14
+ # @params sleep_between_checks [Int]
15
+ # @params logger [Logger]
16
+ # @return [Full360::Sequencer::Runner]
17
+ def initialize(sleep_between_checks, logger = nil)
18
+ @sleep_between_checks = sleep_between_checks
19
+ @logger = logger ||= Logger.new(STDOUT)
15
20
  end
16
21
 
17
22
  def config_from_file(yaml_path)
@@ -20,28 +25,34 @@ module Full360
20
25
 
21
26
  def run_task_class(task_type_string)
22
27
  case task_type_string
23
- when "ecs_task" then Full360::Sequencer::RunECSTask
24
- else nil
28
+ when "ecs_task"
29
+ Full360::Sequencer::RunECSTask
30
+ else
31
+ nil
25
32
  end
26
33
  end
27
34
 
28
35
  def run
29
- @config.each do |params|
36
+ config.each do |params|
30
37
  this_task_name = task_name(params)
31
- this_task = run_task_class(params[this_task_name]["type"]).new(
38
+
39
+ this_task = run_task_class(params[this_task_name][:type]).new(
32
40
  this_task_name,
33
41
  params[this_task_name]
34
42
  )
43
+
35
44
  this_task.run_task
45
+
36
46
  until this_task.completed?
37
- sleep @sleep_between_checks
47
+ sleep sleep_between_checks
38
48
  end
49
+
39
50
  raise "task failed error" unless this_task.success
40
51
  end
41
52
  rescue => e
42
- @logger.error("SEQUENCER_ERROR")
43
- @logger.error(e.message)
44
- e.backtrace.each { |r| @logger.error(r) }
53
+ logger.error("SEQUENCER_ERROR: #{e.message}")
54
+
55
+ e.backtrace.each { |r| logger.error(r) }
45
56
  raise e
46
57
  end
47
58
 
@@ -49,14 +60,20 @@ module Full360
49
60
  params.keys.first
50
61
  end
51
62
 
52
- def parse_config_file(yaml_path)
53
- YAML.load_file(yaml_path)
54
- end
55
-
56
63
  def config_valid?(config)
57
64
  return false unless config.is_a? Array
58
65
  true
59
66
  end
67
+
68
+ private
69
+
70
+ # parse_config_file reads and parses the configuration file and returns
71
+ # an array of hashes with symbolize keys ready to consume.
72
+ # @params yaml_path [String]
73
+ # @return [Array]
74
+ def parse_config_file(yaml_path)
75
+ YAML.safe_load(File.read(yaml_path), symbolize_names: true)
76
+ end
60
77
  end
61
78
  end
62
79
  end
@@ -1,5 +1,5 @@
1
1
  module Full360
2
2
  module Sequencer
3
- VERSION = "0.1.3".freeze
3
+ VERSION = "0.2.7".freeze
4
4
  end
5
5
  end
@@ -1,3 +1,5 @@
1
+ require "logger"
2
+
1
3
  # Autoload all gem classes
2
4
  module Full360
3
5
  module Sequencer
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: full360-sequencer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Full 360 Group
@@ -11,19 +11,19 @@ cert_chain: []
11
11
  date: 2017-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: aws-sdk
14
+ name: aws-sdk-ecs
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.9'
19
+ version: '1.85'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.9'
26
+ version: '1.85'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -78,6 +78,8 @@ files:
78
78
  - ".github/workflows/test.yml"
79
79
  - ".gitignore"
80
80
  - CHANGELOG.md
81
+ - Dockerfile.jruby
82
+ - Dockerfile.ruby
81
83
  - Gemfile
82
84
  - Gemfile.lock
83
85
  - LICENSE
@@ -85,6 +87,7 @@ files:
85
87
  - Rakefile
86
88
  - bin/console
87
89
  - bin/sequencer
90
+ - entrypoint.sh
88
91
  - full360-sequencer.gemspec
89
92
  - lib/full360-sequencer.rb
90
93
  - lib/full360_sequencer.rb