fimon 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0cd75f8af59217142ec5c2c1f98dd7139f6aa588bd68739adc5cf824bd9bd06b
4
+ data.tar.gz: 62011c6a18c7f01b616b0dc0d4fa680d9cce79e6067d320f6e6a10533746f565
5
+ SHA512:
6
+ metadata.gz: 763cc4ed264a546e4aa4d532352af09cea0953efaf051cfa32febf1857dce4f20dd93bb318d0ff9daa75f8dee53fdcf5b670d67cce0c6814f2a91560f189784b
7
+ data.tar.gz: 7459d48f70bf341c9f2c72810a4f5411e3a5254d55cd9af6bd89cebbd7e802df236833245602b36c7518b5ec418570da1de8a25bd7823c47091c700f67f16be9
@@ -0,0 +1,4 @@
1
+ # You can read more about CODEOWNERS at
2
+ # https://help.github.com/github/creating-cloning-and-archiving-repositories/about-code-owners
3
+
4
+ * @fnando
@@ -0,0 +1,4 @@
1
+ # These are supported funding model platforms
2
+ ---
3
+ github: [fnando]
4
+ custom: ["https://paypal.me/nandovieira/๐Ÿ•"]
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: "๐Ÿ› Bug Report"
3
+ about: Report a reproducible bug or regression.
4
+ title: 'Bug: '
5
+ labels: 'Status: Unconfirmed'
6
+
7
+ ---
8
+
9
+ <!--
10
+ - Please provide a clear and concise description of what the bug is.
11
+ - If possible, add an example reproducing your issue.
12
+ - Please test using the latest version of fimon
13
+ to make sure your issue has not already been fixed.
14
+ -->
15
+
16
+ ## Description
17
+
18
+ [Add bug description here]
19
+
20
+ ## How to reproduce
21
+
22
+ [Add steps on how to reproduce this issue]
23
+
24
+ ## What do you expect
25
+
26
+ [Describe what do you expect to happen]
27
+
28
+ ## What happened instead
29
+
30
+ [Describe the actual results]
31
+
32
+ ## Software:
33
+
34
+ - Gem version: [Add gem version here]
35
+ - Ruby version: [Add version here]
36
+
37
+ ## Full backtrace
38
+
39
+ ```text
40
+ [Paste full backtrace here]
41
+ ```
@@ -0,0 +1,5 @@
1
+ ---
2
+ contact_links:
3
+ - name: "๐Ÿคจ Q&A"
4
+ url: https://github.com/fnando/fimon/discussions/new?category=q-a
5
+ about: Have a question? Ask it away here!
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: "๐Ÿ’ก Feature request"
3
+ about: Have an idea that may be useful? Make a suggestion!
4
+ title: 'Feature Request: '
5
+ labels: 'Feature request'
6
+
7
+ ---
8
+
9
+ ## Description
10
+
11
+ _A clear and concise description of what the problem is._
12
+
13
+ ## Describe the solution
14
+
15
+ _A clear and concise description of what you want to happen._
16
+
17
+ ## Alternatives you considered
18
+
19
+ _A clear and concise description of any alternative solutions or features you've considered._
20
+
21
+ ## Additional context
22
+
23
+ _Add any other context, screenshots, links, etc about the feature request here._
@@ -0,0 +1,38 @@
1
+ <!--
2
+ If you're making a doc PR or something tiny where the below is irrelevant,
3
+ delete this template and use a short description, but in your description aim to
4
+ include both what the change is, and why it is being made, with enough context
5
+ for anyone to understand.
6
+ -->
7
+
8
+ <details>
9
+ <summary>PR Checklist</summary>
10
+
11
+ ### PR Structure
12
+
13
+ - [ ] This PR has reasonably narrow scope (if not, break it down into smaller
14
+ PRs).
15
+ - [ ] This PR avoids mixing refactoring changes with feature changes (split into
16
+ two PRs otherwise).
17
+ - [ ] This PR's title starts is concise and descriptive.
18
+
19
+ ### Thoroughness
20
+
21
+ - [ ] This PR adds tests for the most critical parts of the new functionality or
22
+ fixes.
23
+ - [ ] I've updated any docs, `.md` files, etcโ€ฆ affected by this change.
24
+
25
+ </details>
26
+
27
+ ### What
28
+
29
+ [TODO: Short statement about what is changing.]
30
+
31
+ ### Why
32
+
33
+ [TODO: Why this change is being made. Include any context required to understand
34
+ the why.]
35
+
36
+ ### Known limitations
37
+
38
+ [TODO or N/A]
@@ -0,0 +1,15 @@
1
+ ---
2
+ # Documentation:
3
+ # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
4
+
5
+ version: 2
6
+ updates:
7
+ - package-ecosystem: "github-actions"
8
+ directory: "/"
9
+ schedule:
10
+ interval: "daily"
11
+
12
+ - package-ecosystem: bundler
13
+ directory: "/"
14
+ schedule:
15
+ interval: "daily"
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: ruby-tests
3
+
4
+ on:
5
+ pull_request_target:
6
+ push:
7
+ branches:
8
+ - main
9
+ workflow_dispatch:
10
+ inputs: {}
11
+
12
+ jobs:
13
+ build:
14
+ name: Tests with Ruby ${{ matrix.ruby }} and ${{ matrix.gemfile }}
15
+ runs-on: "ubuntu-latest"
16
+ if: |
17
+ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request_target' ||
18
+ github.actor != 'dependabot[bot]'
19
+ strategy:
20
+ fail-fast: false
21
+ matrix:
22
+ ruby: ["2.7", "3.0", "3.1"]
23
+ gemfile:
24
+ - Gemfile
25
+
26
+ steps:
27
+ - uses: actions/checkout@v1
28
+
29
+ - uses: actions/cache@v2
30
+ with:
31
+ path: vendor/bundle
32
+ key: >
33
+ ${{ runner.os }}-${{ matrix.ruby }}-gems-${{ hashFiles(matrix.gemfile) }}
34
+
35
+ - name: Set up Ruby
36
+ uses: ruby/setup-ruby@v1
37
+ with:
38
+ ruby-version: ${{ matrix.ruby }}
39
+
40
+ - name: Install gem dependencies
41
+ env:
42
+ BUNDLE_GEMFILE: ${{ matrix.gemfile }}
43
+ run: |
44
+ gem install bundler
45
+ bundle config path vendor/bundle
46
+ bundle update --jobs 4 --retry 3
47
+
48
+ - name: Run Tests
49
+ env:
50
+ BUNDLE_GEMFILE: ${{ matrix.gemfile }}
51
+ run: |
52
+ bundle exec rake
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
+ /vendor/
10
+ *.log
11
+ *.lock
data/.rubocop.yml ADDED
@@ -0,0 +1,10 @@
1
+ ---
2
+ inherit_gem:
3
+ rubocop-fnando: .rubocop.yml
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 3.1
7
+ NewCops: enable
8
+ Exclude:
9
+ - vendor/**/*
10
+ - gemfiles/**/*
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ <!--
4
+ Prefix your message with one of the following:
5
+
6
+ - [Added] for new features.
7
+ - [Changed] for changes in existing functionality.
8
+ - [Deprecated] for soon-to-be removed features.
9
+ - [Removed] for now removed features.
10
+ - [Fixed] for any bug fixes.
11
+ - [Security] in case of vulnerabilities.
12
+ -->
13
+
14
+ ## Unreleased
15
+
16
+ - Initial release.
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at me@fnando.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,79 @@
1
+ # Contributing to fimon
2
+
3
+ ๐Ÿ‘๐ŸŽ‰ First off, thanks for taking the time to contribute! ๐ŸŽ‰๐Ÿ‘
4
+
5
+ The following is a set of guidelines for contributing to this project. These are
6
+ mostly guidelines, not rules. Use your best judgment, and feel free to propose
7
+ changes to this document in a pull request.
8
+
9
+ ## Code of Conduct
10
+
11
+ Everyone interacting in this project's codebases, issue trackers, chat rooms and
12
+ mailing lists is expected to follow the [code of conduct](https://github.com/fnando/fimon/blob/main/CODE_OF_CONDUCT.md).
13
+
14
+ ## Reporting bugs
15
+
16
+ This section guides you through submitting a bug report. Following these
17
+ guidelines helps maintainers and the community understand your report, reproduce
18
+ the behavior, and find related reports.
19
+
20
+ - Before creating bug reports, please check the open issues; somebody may
21
+ already have submitted something similar, and you may not need to create a new
22
+ one.
23
+ - When you are creating a bug report, please include as many details as
24
+ possible, with an example reproducing the issue.
25
+
26
+ ## Contributing with code
27
+
28
+ Before making any radicals changes, please make sure you discuss your intention
29
+ by [opening an issue on Github](https://github.com/fnando/fimon/issues).
30
+
31
+ When you're ready to make your pull request, follow checklist below to make sure
32
+ your contribution is according to how this project works.
33
+
34
+ 1. [Fork](https://help.github.com/forking/) fimon
35
+ 2. Create a topic branch - `git checkout -b my_branch`
36
+ 3. Make your changes using [descriptive commit messages](#commit-messages)
37
+ 4. Update CHANGELOG.md describing your changes by adding an entry to the
38
+ "Unreleased" section. If this section is not available, create one right
39
+ before the last version.
40
+ 5. Push to your branch - `git push origin my_branch`
41
+ 6. [Create a pull request](https://help.github.com/articles/creating-a-pull-request)
42
+ 7. That's it!
43
+
44
+ ## Styleguides
45
+
46
+ ### Commit messages
47
+
48
+ - Use the present tense ("Add feature" not "Added feature")
49
+ - Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
50
+ - Limit the first line to 72 characters or less
51
+ - Reference issues and pull requests liberally after the first line
52
+
53
+ ### Changelog
54
+
55
+ - Add a message describing your changes to the "Unreleased" section. The
56
+ changelog message should follow the same style as the commit message.
57
+ - Prefix your message with one of the following:
58
+ - `[Added]` for new features.
59
+ - `[Changed]` for changes in existing functionality.
60
+ - `[Deprecated]` for soon-to-be removed features.
61
+ - `[Removed]` for now removed features.
62
+ - `[Fixed]` for any bug fixes.
63
+ - `[Security]` in case of vulnerabilities.
64
+
65
+ ### Ruby code
66
+
67
+ - This project uses [Rubocop](https://rubocop.org) to enforce code style. Before
68
+ submitting your changes, make sure your tests are passing and code conforms to
69
+ the expected style by running `rake`.
70
+ - Do not change the library version. This will be done by the maintainer
71
+ whenever a new version is about to be released.
72
+
73
+ ### JavaScript code
74
+
75
+ - This project uses [ESLint](https://eslint.org) to enforce code style. Before
76
+ submitting your changes, make sure your tests are passing and code conforms to
77
+ the expected style by running `yarn test:ci`.
78
+ - Do not change the library version. This will be done by the maintainer
79
+ whenever a new version is about to be released.
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "minitest"
6
+ gem "minitest-utils"
7
+ gem "pry-meta"
8
+ gem "rake"
9
+ gem "rubocop"
10
+ gem "rubocop-fnando"
11
+ gem "simplecov"
12
+
13
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ # The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Nando Vieira
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ 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, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # fimon
2
+
3
+ [![Tests](https://github.com/fnando/fimon/workflows/ruby-tests/badge.svg)](https://github.com/fnando/fimon)
4
+ [![Gem](https://img.shields.io/gem/v/fimon.svg)](https://rubygems.org/gems/fimon)
5
+ [![Gem](https://img.shields.io/gem/dt/fimon.svg)](https://rubygems.org/gems/fimon)
6
+ [![MIT License](https://img.shields.io/:License-MIT-blue.svg)](https://tldrlegal.com/license/mit-license)
7
+
8
+ Thin `listen` cli wrapper.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ gem install fimon
14
+ ```
15
+
16
+ Or add the following line to your project's Gemfile:
17
+
18
+ ```ruby
19
+ gem "fimon"
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ TODO: Write gem usage
25
+
26
+ ## Maintainer
27
+
28
+ - [Nando Vieira](https://github.com/fnando)
29
+
30
+ ## Contributors
31
+
32
+ - https://github.com/fnando/fimon/contributors
33
+
34
+ ## Contributing
35
+
36
+ For more details about how to contribute, please read
37
+ https://github.com/fnando/fimon/blob/main/CONTRIBUTING.md.
38
+
39
+
40
+ ## License
41
+
42
+ The gem is available as open source under the terms of the
43
+ [MIT License](https://opensource.org/licenses/MIT). A copy of the license can be
44
+ found at https://github.com/fnando/fimon/blob/main/LICENSE.md.
45
+
46
+ ## Code of Conduct
47
+
48
+ Everyone interacting in the fimon project's codebases, issue trackers,
49
+ chat rooms and mailing lists is expected to follow the
50
+ [code of conduct](https://github.com/fnando/fimon/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+ require "rubocop/rake_task"
6
+
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << "test"
9
+ t.libs << "lib"
10
+ t.test_files = FileList["test/**/*_test.rb"]
11
+ end
12
+
13
+ RuboCop::RakeTask.new
14
+
15
+ task default: %i[test rubocop]
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "fimon"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ begin
11
+ require "pry"
12
+ Pry.start
13
+ rescue LoadError
14
+ require "irb"
15
+ IRB.start(__FILE__)
16
+ end
data/bin/setup ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ IFS=$'\n\t'
6
+ set -vx
7
+
8
+ bundle install
9
+
10
+ # Do any other automated setup that you need to do here
data/exe/fimon ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/fimon"
5
+ Fimon::CLI.start
data/fimon.gemspec ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./lib/fimon/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "fimon"
7
+ spec.version = Fimon::VERSION
8
+ spec.authors = ["Nando Vieira"]
9
+ spec.email = ["me@fnando.com"]
10
+ spec.metadata = {"rubygems_mfa_required" => "true"}
11
+
12
+ spec.summary = "Thin `listen` cli wrapper."
13
+ spec.description = spec.summary
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
16
+
17
+ github_url = "https://github.com/fnando/fimon"
18
+ github_tree_url = "#{github_url}/tree/v#{spec.version}"
19
+
20
+ spec.homepage = github_url
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["bug_tracker_uri"] = "#{github_url}/issues"
23
+ spec.metadata["source_code_uri"] = github_tree_url
24
+ spec.metadata["changelog_uri"] = "#{github_tree_url}/CHANGELOG.md"
25
+ spec.metadata["documentation_uri"] = "#{github_tree_url}/README.md"
26
+ spec.metadata["license_uri"] = "#{github_tree_url}/LICENSE.md"
27
+
28
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
29
+ `git ls-files -z`
30
+ .split("\x0")
31
+ .reject {|f| f.match(%r{^(test|spec|features)/}) }
32
+ end
33
+
34
+ spec.bindir = "exe"
35
+ spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f) }
36
+ spec.require_paths = ["lib"]
37
+
38
+ spec.add_dependency "listen"
39
+ spec.add_dependency "thor"
40
+ end
data/lib/fimon/cli.rb ADDED
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fimon
4
+ class CLI < Thor
5
+ check_unknown_options!
6
+
7
+ def self.exit_on_failure?
8
+ true
9
+ end
10
+
11
+ desc "init", "Create the configuration file"
12
+ option :config,
13
+ type: :string,
14
+ default: "config/fimon.yml",
15
+ desc: "The configuration file path"
16
+ option :force,
17
+ type: :boolean,
18
+ default: false,
19
+ desc: "Force the file creation, even if there's config file already"
20
+ def init
21
+ config_file = options[:config]
22
+
23
+ if File.file?(config_file) && !options[:force]
24
+ interrupt_with_error("The config file #{config_file} already exists")
25
+ end
26
+
27
+ FileUtils.mkdir_p(File.dirname(config_file))
28
+ FileUtils.cp File.join(__dir__, "templates/fimon.yml"), config_file
29
+ end
30
+
31
+ desc "start", "Start fimon and listens to file changes"
32
+ option :run_on_start,
33
+ type: :boolean,
34
+ default: true,
35
+ desc: "Runs commands on boot"
36
+ option :config,
37
+ type: :string,
38
+ default: "config/fimon.yml",
39
+ desc: "The configuration file path"
40
+ def start
41
+ unless File.file?(options[:config])
42
+ interrupt_with_error(
43
+ "The config file #{options[:config]} doesn't exist"
44
+ )
45
+ end
46
+
47
+ Fimon.listen(
48
+ config_file: options[:config],
49
+ run_on_start: options[:run_on_start]
50
+ )
51
+ end
52
+
53
+ no_commands do
54
+ private def interrupt_with_error(message)
55
+ shell.say "ERROR: #{message}", :red
56
+ exit 1
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fimon
4
+ class Generator < Thor::Group
5
+ include Thor::Actions
6
+
7
+ attr_accessor :options
8
+
9
+ def self.source_root
10
+ File.join(__dir__, "templates")
11
+ end
12
+
13
+ def create_config_file
14
+ copy_file "fimon.yml", options[:config]
15
+ end
16
+
17
+ no_commands do
18
+ # Add helper methods here
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fimon
4
+ class Listener
5
+ attr_accessor :started, :config_file, :run_on_start, :options
6
+
7
+ def initialize(config_file:, run_on_start:, options: {})
8
+ @config_file = config_file
9
+ @run_on_start = run_on_start
10
+ @options = options || {}
11
+ end
12
+
13
+ def config
14
+ @config ||= begin
15
+ config = YAML.load_file(config_file, symbolize_names: true)
16
+
17
+ info("Loaded config file: #{config_file}")
18
+
19
+ config[:triggers] = Array(config[:triggers]).map do |trigger|
20
+ trigger.merge(
21
+ matcher: Matcher.new(trigger[:pattern]),
22
+ ignore:
23
+ Array(trigger[:ignore]).map {|pattern| Matcher.new(pattern) }
24
+ )
25
+ end
26
+
27
+ config
28
+ end
29
+ end
30
+
31
+ def listen
32
+ return if started
33
+
34
+ $stdout.sync = true
35
+ self.started = true
36
+
37
+ listen_options = options.merge(
38
+ ignore: Array(config[:ignore]).map {|entry| ignore_regex(entry) }
39
+ )
40
+
41
+ stop = false
42
+
43
+ run_triggers_on_start if run_on_start
44
+
45
+ listener(listen_options:).start
46
+ Signal.trap("SIGINT") { stop = true }
47
+ Signal.trap("SIGTERM") { stop = true }
48
+
49
+ loop do
50
+ if stop
51
+ info("Exiting")
52
+ break
53
+ end
54
+
55
+ sleep 1
56
+ end
57
+ end
58
+
59
+ def root_dir
60
+ @root_dir ||= Pathname.new(Dir.pwd)
61
+ end
62
+
63
+ def run_triggers_on_start
64
+ threads = config[:triggers].map do |trigger|
65
+ Thread.new do
66
+ next if trigger[:run_on_start] == false
67
+
68
+ cmd = Array(trigger[:command])
69
+
70
+ next if cmd.include?("$file")
71
+
72
+ name = trigger.fetch(:name, cmd.first)
73
+ cmd_string = to_cmd_string(cmd)
74
+
75
+ status, error = *run_cmd(cmd)
76
+
77
+ if error
78
+ info("The command \"#{cmd_string}\" has errored with <#{error.class.name}: #{error.message}>")
79
+ next
80
+ end
81
+
82
+ if status.success?
83
+ info("\"#{name}\" has been triggered")
84
+ else
85
+ info("The command \"#{cmd_string}\" has failed with status=#{status.exitstatus}")
86
+ end
87
+ end
88
+ end
89
+
90
+ threads.each(&:join)
91
+ end
92
+
93
+ def run_cmd(cmd)
94
+ _, _, status = *Open3.capture3(*cmd)
95
+ [status, nil]
96
+ rescue StandardError => error
97
+ [nil, error]
98
+ end
99
+
100
+ def ignore_regex(entry)
101
+ Regexp.new(Regexp.escape(entry))
102
+ end
103
+
104
+ def relative_path(path)
105
+ Pathname.new(path).relative_path_from(root_dir).to_s
106
+ end
107
+
108
+ def relative_path_list(paths)
109
+ paths.map {|path| relative_path(path) }
110
+ end
111
+
112
+ def debug(message)
113
+ puts "=> #{message}"
114
+ end
115
+
116
+ def info(message)
117
+ puts "=> #{message}"
118
+ end
119
+
120
+ def to_cmd_string(cmd)
121
+ cmd.map {|arg| Shellwords.escape(arg) }.join(" ")
122
+ end
123
+
124
+ def listener(listen_options:)
125
+ Listen.to(
126
+ File.dirname(config_file),
127
+ root_dir,
128
+ listen_options
129
+ ) do |changed, added, removed|
130
+ @config = nil if changed.include?(File.expand_path(config_file))
131
+
132
+ changes = compute_changes(changed:, added:, removed:)
133
+
134
+ next unless changes.any?
135
+
136
+ executed = []
137
+
138
+ config[:triggers].each do |trigger|
139
+ changes.values.flatten.each do |path|
140
+ next unless trigger[:matcher].match?(path)
141
+ next if trigger[:ignore].any? {|pattern| pattern.match?(path) }
142
+
143
+ cmd = Array(trigger[:command]).map do |arg|
144
+ arg == "$file" ? path : arg
145
+ end
146
+
147
+ name = trigger.fetch(:name, cmd.first)
148
+
149
+ cmd_string = to_cmd_string(cmd)
150
+
151
+ digest = Digest::MD5.hexdigest(cmd.inspect)
152
+
153
+ if executed.include?(digest)
154
+ info("The command \"#{cmd_string}\" was already executed")
155
+ next
156
+ end
157
+
158
+ status, error = *run_cmd(cmd)
159
+
160
+ if error
161
+ info(
162
+ "The command \"#{cmd_string}\" has errored with " \
163
+ "<#{error.class.name}: #{error.message}>"
164
+ )
165
+ next
166
+ end
167
+
168
+ if status.success?
169
+ executed << digest
170
+
171
+ info("\"#{name}\" was triggered by #{path.inspect}")
172
+ else
173
+ info(
174
+ "The command \"#{cmd_string}\" has failed with " \
175
+ "status=#{status.exitstatus}"
176
+ )
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ def compute_changes(changed:, added:, removed:)
184
+ changed = changed.map {|path| relative_path(path) }
185
+ added = added.map {|path| relative_path(path) }
186
+ removed = removed.map {|path| relative_path(path) }
187
+
188
+ {
189
+ changed: included_on_watched_paths(changed),
190
+ added: included_on_watched_paths(added),
191
+ removed: included_on_watched_paths(removed)
192
+ }.select {|_k, v| v.any? }
193
+ end
194
+
195
+ def included_on_watched_paths(changes)
196
+ changes.select do |change|
197
+ config[:triggers].any? do |trigger|
198
+ trigger[:matcher].match?(change)
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fimon
4
+ class Matcher
5
+ def initialize(patterns)
6
+ @patterns = Array(patterns).map {|pattern| to_regex(pattern) }
7
+ end
8
+
9
+ def match?(input)
10
+ @patterns.any? {|pattern| pattern.match?(input) }
11
+ end
12
+ alias =~ match?
13
+
14
+ private def to_regex(pattern)
15
+ double = "__double__"
16
+ single = "__single__"
17
+
18
+ extension_groups = pattern.scan(/\{(.*?)\}/).flatten
19
+
20
+ extension_groups.each_with_index do |group, index|
21
+ pattern = pattern.gsub("{#{group}}", "__#{index}__")
22
+ end
23
+
24
+ pattern = pattern
25
+ .gsub("**/", double)
26
+ .gsub("*", single)
27
+
28
+ pattern = Regexp.escape(pattern)
29
+ .gsub(double, "([^/]+/)*")
30
+ .gsub(single, "[^/]+")
31
+
32
+ extension_groups.each_with_index do |group, i|
33
+ pattern = pattern.gsub("__#{i}__", "(#{group.split(',').join('|')})")
34
+ end
35
+
36
+ Regexp.new("^#{pattern}$")
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,51 @@
1
+ ---
2
+ ignore:
3
+ - /node_modules/
4
+ - /tmp/
5
+ - /log/
6
+ - /some/file.txt
7
+
8
+ triggers:
9
+ - name: Rails initializers
10
+ pattern: "config/initializers/*.rb"
11
+ command: [touch, tmp/restart.txt]
12
+ run_on_start: false
13
+
14
+ - name: Rails config files
15
+ pattern: "config/*.rb"
16
+ command: [touch, tmp/restart.txt]
17
+ ignore:
18
+ - "config/routes.rb"
19
+ run_on_start: false
20
+
21
+ - name: Translation files
22
+ pattern: "config/locales/**/*.{yml,rb,po}"
23
+ command: [bundle, exec, i18n, export]
24
+
25
+ - name: Seagull templates
26
+ pattern: "app/javascript/**/*.sea"
27
+ command:
28
+ [
29
+ seagull,
30
+ compile,
31
+ --input,
32
+ app/javascript/**/*.sea,
33
+ --output,
34
+ app/javascript/helpers/templates.js,
35
+ ]
36
+
37
+ - name: Rails routes
38
+ pattern: config/routes.rb
39
+ command: [enroute, export, --output, app/javascript/config/routes.ts]
40
+
41
+ - name: SVG files
42
+ pattern: app/images/**/*.svg
43
+ command: ./bin/sprites
44
+
45
+ - name: Stylesheets
46
+ pattern: app/assets/styleshets/**/*.css
47
+ command: yarn build:css
48
+
49
+ - name: Scripts
50
+ pattern: app/javascript/**/*.{ts,js}
51
+ command: yarn build:js
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fimon
4
+ VERSION = "0.0.0"
5
+ end
data/lib/fimon.rb ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "listen"
5
+ require "pathname"
6
+ require "logger"
7
+ require "fileutils"
8
+ require "yaml"
9
+ require "open3"
10
+ require "digest/md5"
11
+ require "shellwords"
12
+
13
+ module Fimon
14
+ require_relative "fimon/version"
15
+ require_relative "fimon/cli"
16
+ require_relative "fimon/generator"
17
+ require_relative "fimon/listener"
18
+ require_relative "fimon/matcher"
19
+
20
+ def self.listen(**kwargs)
21
+ Listener.new(**kwargs).listen
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fimon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Nando Vieira
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-09-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: listen
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Thin `listen` cli wrapper.
42
+ email:
43
+ - me@fnando.com
44
+ executables:
45
+ - fimon
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".github/CODEOWNERS"
50
+ - ".github/FUNDING.yml"
51
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
52
+ - ".github/ISSUE_TEMPLATE/config.yml"
53
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
54
+ - ".github/PULL_REQUEST_TEMPLATE.md"
55
+ - ".github/dependabot.yml"
56
+ - ".github/workflows/ruby-tests.yml"
57
+ - ".gitignore"
58
+ - ".rubocop.yml"
59
+ - CHANGELOG.md
60
+ - CODE_OF_CONDUCT.md
61
+ - CONTRIBUTING.md
62
+ - Gemfile
63
+ - LICENSE.md
64
+ - README.md
65
+ - Rakefile
66
+ - bin/console
67
+ - bin/setup
68
+ - exe/fimon
69
+ - fimon.gemspec
70
+ - lib/fimon.rb
71
+ - lib/fimon/cli.rb
72
+ - lib/fimon/generator.rb
73
+ - lib/fimon/listener.rb
74
+ - lib/fimon/matcher.rb
75
+ - lib/fimon/templates/fimon.yml
76
+ - lib/fimon/version.rb
77
+ homepage: https://github.com/fnando/fimon
78
+ licenses:
79
+ - MIT
80
+ metadata:
81
+ rubygems_mfa_required: 'true'
82
+ homepage_uri: https://github.com/fnando/fimon
83
+ bug_tracker_uri: https://github.com/fnando/fimon/issues
84
+ source_code_uri: https://github.com/fnando/fimon/tree/v0.0.0
85
+ changelog_uri: https://github.com/fnando/fimon/tree/v0.0.0/CHANGELOG.md
86
+ documentation_uri: https://github.com/fnando/fimon/tree/v0.0.0/README.md
87
+ license_uri: https://github.com/fnando/fimon/tree/v0.0.0/LICENSE.md
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 3.1.0
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubygems_version: 3.4.6
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Thin `listen` cli wrapper.
107
+ test_files: []