gitmqueue 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7e42924f796ddd596ee51c69e7d837da4988ce63db27688998e43260570ad9ec
4
+ data.tar.gz: 8314d9f06183362f39f4831dc8442dfaaeb879bb23860bab6bc8cdf9ea7ebb98
5
+ SHA512:
6
+ metadata.gz: c9459c7b8990fe2c62f24d5bdcdd0c50be3e1afe14fb6dc5ac920aace0ecc79581eeccf57d892bd32f29d7585bcbea895fa0e3b909e850796bb25d5075cb4c14
7
+ data.tar.gz: 541f339f68307c55e27cbc182e716dcd591c8d9bc5a018be4e7a7563d1a7bec1239ac85303b2c419791a7142cade0d1b6fc586875adb29717903aefafe34a915
@@ -0,0 +1,31 @@
1
+ name: Ruby Gem
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - master
7
+ push:
8
+ branches:
9
+ - master
10
+
11
+ jobs:
12
+ build:
13
+ name: Build + Publish
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - uses: actions/checkout@master
18
+ - name: Set up Ruby 2.6
19
+ uses: actions/setup-ruby@v1
20
+ with:
21
+ version: 2.6.x
22
+ - name: Publish to RubyGems
23
+ run: |
24
+ mkdir -p $HOME/.gem
25
+ touch $HOME/.gem/credentials
26
+ chmod 0600 $HOME/.gem/credentials
27
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
28
+ gem build *.gemspec
29
+ gem push *.gem
30
+ env:
31
+ GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.reek.yml ADDED
@@ -0,0 +1,3 @@
1
+ detectors:
2
+ IrresponsibleModule:
3
+ enabled: false
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,6 @@
1
+ AllCops:
2
+ DefaultFormatter: simple
3
+ Style/Documentation:
4
+ Enabled: false
5
+ Naming/RescuedExceptionsVariableName:
6
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.3
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.3
7
+ before_install: gem install bundler -v 2.0.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in gitmq.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gitmqueue (0.1.0)
5
+ rugged (~> 0.28)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.3)
11
+ rake (10.5.0)
12
+ rspec (3.9.0)
13
+ rspec-core (~> 3.9.0)
14
+ rspec-expectations (~> 3.9.0)
15
+ rspec-mocks (~> 3.9.0)
16
+ rspec-core (3.9.0)
17
+ rspec-support (~> 3.9.0)
18
+ rspec-expectations (3.9.0)
19
+ diff-lcs (>= 1.2.0, < 2.0)
20
+ rspec-support (~> 3.9.0)
21
+ rspec-mocks (3.9.0)
22
+ diff-lcs (>= 1.2.0, < 2.0)
23
+ rspec-support (~> 3.9.0)
24
+ rspec-support (3.9.0)
25
+ rugged (0.28.3.1)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ bundler (~> 2.0)
32
+ gitmqueue!
33
+ rake (~> 10.0)
34
+ rspec (~> 3.0)
35
+
36
+ BUNDLED WITH
37
+ 2.0.2
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Emad Elsaid
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # GitMQueue : Git Message Queue
2
+
3
+ Hey, Good idea bad pitch, lets use Git as a messaging queue! lets use github to
4
+ synchronize a cluster of them together.
5
+
6
+ Lets get the obvious out of the way, You already know that we use Git to keep
7
+ track of our code changes, and we need a central point to pull and push these
8
+ changes (although it's designed to be decentralized), this is why we use
9
+ github/lab/bitbucket...etc.
10
+
11
+ Alright, now lets introduce another idea: Git commits doesn't need to include
12
+ changes, so you don't have to create files then commit these files to
13
+ your git repo, you can create an empty commit with a command like this:
14
+
15
+ ```
16
+ git commit --allow-empty -m "commit message"
17
+ ```
18
+
19
+ OK, so the commit message can be any text right? that means you can have JSON
20
+ messages or YAML or any other machine readable format as a commit message.
21
+
22
+ So now imagine this scenario:
23
+
24
+ 1. Create a repo on github
25
+ 1. Clone it to your server
26
+ 1. Have your application commit a `message` to this repo every time you need to
27
+ 1. Push the repo every period say 10 seconds
28
+ 1. Clone same repo to another server
29
+ 1. Have an application there (consumer) pull periodically
30
+ 1. Have Another thread there that checks for new commits and for each one of
31
+ them extracts the message and does the job
32
+ 1. Put a git tag on the last processed message with that consumer identifier
33
+ 1. Push that label to keep track of the last processed message (pull it when you
34
+ deploy that consumer again)
35
+
36
+ Now you have a queue of messages that can't be altered, communicated between
37
+ servers and all data saved in a git repository.
38
+
39
+ This idea is flexible and can be modified for several use cases:
40
+
41
+ 1. the central server doesn't have to be a github or a like, you can use your
42
+ own server as a git server.
43
+ 1. You can have more than one queue for messages by creating many branches each
44
+ for a type of messages or a different messages purpose.
45
+ 1. You can have the same branch consumed from different servers each message
46
+ processed multiple times in different ways like RabbitMQ fan out strategy
47
+ 1. You can keep browse your message queue with any git client UI or CLI
48
+ 1. You can clean your servers git local repo so that it deletes old commit
49
+ history to save space.
50
+ 1. You can have many messages producers write to different branches in the same
51
+ repository
52
+ 1. In the future you can join 2 branches or split a branch and have your
53
+ consumers adapt to that
54
+
55
+
56
+ There are many advantages to use this approach, which is inherited from how Git
57
+ is designed
58
+
59
+ 1. Every message has an address (git commit hash)
60
+ 1. Every message can be traced to an author which is the application that
61
+ published it (you can use different names for each server or application
62
+ version)
63
+ 1. Every message has a creation date
64
+
65
+ This idea can as simple as invoking git commands from your application with a
66
+ system call, or use libgit to manipulate the repository for you, or even a small
67
+ server application that communicate over a socket, or a repo service that
68
+ communicate over even HTTP, there are so many ways to implement that concept.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'gitmqueue'
5
+
6
+ path = ARGV.shift
7
+ branch = ARGV.shift
8
+ consumer = ARGV.shift
9
+
10
+ consumer = GitMQueue::Consumer.new(
11
+ storage: GitMQueue::Storage.new(path),
12
+ name: consumer,
13
+ branch: branch
14
+ )
15
+
16
+ begin
17
+ consumer.consume { |m| puts m }
18
+ rescue Interrupt
19
+ puts 'Bye.'
20
+ end
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'gitmqueue'
5
+
6
+ path = ARGV.shift
7
+ branch = ARGV.shift
8
+
9
+ producer = GitMQueue::Producer.new(
10
+ storage: GitMQueue::Storage.new(path),
11
+ branch: branch
12
+ )
13
+
14
+ loop do
15
+ producer.publish(gets.strip)
16
+ rescue Interrupt
17
+ puts 'Bye.'
18
+ exit
19
+ end
data/gitmqueue.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'gitmqueue'
9
+ spec.version = GitMQueue::VERSION
10
+ spec.authors = ['Emad Elsaid']
11
+ spec.email = ['emad.elsaid.hamed@gmail.com']
12
+
13
+ spec.summary = 'Git Message Queue'
14
+ spec.description = <<-DESC
15
+ An interface to use Git repositories as a message queue to communicate between services'
16
+ DESC
17
+ spec.homepage = 'https://www.github.com/emad-elsaid/gitmqueue'
18
+ spec.license = 'MIT'
19
+
20
+ spec.metadata['homepage_uri'] = spec.homepage
21
+ spec.metadata['source_code_uri'] = spec.homepage
22
+
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
25
+ end
26
+ spec.bindir = 'bin'
27
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_dependency 'rugged', '~> 0.28'
31
+ spec.add_development_dependency 'bundler', '~> 2.0'
32
+ spec.add_development_dependency 'rake', '~> 10.0'
33
+ spec.add_development_dependency 'rspec', '~> 3.0'
34
+ end
data/lib/consumer.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitMQueue
4
+ class Consumer
5
+ def initialize(storage:, name:, branch:)
6
+ @storage = storage
7
+ @branch = branch
8
+ @name = name
9
+ @label = "#{branch}.#{name}"
10
+ end
11
+
12
+ def consume(&block)
13
+ @storage.wait_branch @branch
14
+
15
+ loop do
16
+ commit = @storage.poll(@branch, @label)
17
+ block.call commit.message
18
+ @storage.tag(@label, commit)
19
+ end
20
+ end
21
+ end
22
+ end
data/lib/gitmqueue.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './version'
4
+ require_relative './consumer'
5
+ require_relative './producer'
6
+ require_relative './storage'
7
+
8
+ module GitMQueue
9
+ end
data/lib/producer.rb ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitMQueue
4
+ class Producer
5
+ def initialize(storage:, branch:)
6
+ @storage = storage
7
+ @branch = branch
8
+ end
9
+
10
+ def publish(event)
11
+ commit = Rugged::Commit.create(
12
+ @storage.repo,
13
+ tree: @storage.tree,
14
+ message: event.to_s,
15
+ parents: [@storage.branch(@branch)&.target].compact
16
+ )
17
+ @storage.branches.create(@branch, commit, force: true)
18
+ end
19
+ end
20
+ end
data/lib/storage.rb ADDED
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'rugged'
5
+
6
+ module GitMQueue
7
+ class Storage
8
+ WAIT_NON_EXISTING_BRANCH = 1 # seconds
9
+ WAIT_FOR_COMMIT = 1 # seconds
10
+
11
+ attr_reader :repo
12
+
13
+ def initialize(path)
14
+ @path = path
15
+ @repo = read_or_create_repo
16
+ end
17
+
18
+ def branches
19
+ repo.branches
20
+ end
21
+
22
+ def branch(name)
23
+ branches[name]
24
+ end
25
+
26
+ def tree
27
+ Rugged::Tree::Builder.new(repo).write
28
+ end
29
+
30
+ def poll(branch, tag)
31
+ wait_for_commit(branch, tag)
32
+ walker(branch, tag).first
33
+ end
34
+
35
+ def tag(label, commit)
36
+ repo.tags.create(label, commit, true)
37
+ end
38
+
39
+ def wait_branch(branch)
40
+ sleep WAIT_NON_EXISTING_BRANCH until repo.branches.exist?(branch)
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :path
46
+
47
+ def walker(branch, tag)
48
+ walker = Rugged::Walker.new(repo)
49
+ walker.sorting(Rugged::SORT_TOPO | Rugged::SORT_REVERSE)
50
+ walker.push(branch(branch).target)
51
+
52
+ tag = repo.tags[tag]
53
+ walker.hide(tag.target) if tag
54
+
55
+ walker
56
+ end
57
+
58
+ def read_or_create_repo
59
+ FileUtils.mkdir_p(path) unless File.exist?(path)
60
+ begin
61
+ Rugged::Repository.new(path)
62
+ rescue Rugged::RepositoryError
63
+ Rugged::Repository.init_at(path, :bare)
64
+ end
65
+ end
66
+
67
+ def wait_for_commit(branch, tag)
68
+ sleep WAIT_FOR_COMMIT until branch(branch).target.oid != repo.tags[tag]&.target&.oid
69
+ end
70
+ end
71
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitMQueue
4
+ VERSION = '0.1.0'
5
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitmqueue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Emad Elsaid
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-11-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rugged
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.28'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.28'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: " An interface to use Git repositories as a message queue to communicate
70
+ between services'\n"
71
+ email:
72
+ - emad.elsaid.hamed@gmail.com
73
+ executables:
74
+ - gitmqueue-consume
75
+ - gitmqueue-produce
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".github/workflows/gempush.yml"
80
+ - ".gitignore"
81
+ - ".reek.yml"
82
+ - ".rspec"
83
+ - ".rubocop.yml"
84
+ - ".ruby-version"
85
+ - ".travis.yml"
86
+ - Gemfile
87
+ - Gemfile.lock
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - bin/gitmqueue-consume
92
+ - bin/gitmqueue-produce
93
+ - gitmqueue.gemspec
94
+ - lib/consumer.rb
95
+ - lib/gitmqueue.rb
96
+ - lib/producer.rb
97
+ - lib/storage.rb
98
+ - lib/version.rb
99
+ homepage: https://www.github.com/emad-elsaid/gitmqueue
100
+ licenses:
101
+ - MIT
102
+ metadata:
103
+ homepage_uri: https://www.github.com/emad-elsaid/gitmqueue
104
+ source_code_uri: https://www.github.com/emad-elsaid/gitmqueue
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubygems_version: 3.0.3
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: Git Message Queue
124
+ test_files: []