fluid_cli 0.1.9 → 0.2.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: 77caec2dd9193b287504a5ebb36eb91746f4a4f5f13e37403e0af2ac89f8dcf2
4
- data.tar.gz: 1de9dc376d3f1c151bd47ff897305f55fc3c13d6409d3202676e394fd7a41e98
3
+ metadata.gz: 84ff5fea48f57756d085af8e58148aec8b8b98161b152f5c7e95252f51e2348b
4
+ data.tar.gz: 0b65b1eccfcf8e095961e4518eb9c5e73cb4b6884b029f12074bfe477b1dc40a
5
5
  SHA512:
6
- metadata.gz: 0da7f1602d15d1ba32f8995039a2d4d40cb2292f2546fbf941f8dcedbded6b5b73df918df068045d815f10bbb2a2377d6b2600beeed87b3cf952d7236a7970a6
7
- data.tar.gz: 46ac939cb3df539f6b447e127487d80aa59c39ac995f17e1f1b858bd3c68e255ec2c28d922c117720c8da520efa77787d3c9b292a5157907f41b821217762282
6
+ metadata.gz: a951d7bfafd5ce072e8b2b8e91f308878021a13697848f0eb6fa294f09e5e0d2118a02339530914fa3dcd0647b2c7e7f29bf36bc4d2f89adcfebaf3e3b225937
7
+ data.tar.gz: 1031eb13ffbfb6025e7adbea17dca9bc76d7ab220840e3a8e5782422ba2edba49ee8dc8ccbc7a631e244e04aca5d62718db9f1d85043ec4ad5770008c00e2533
data/README.md CHANGED
@@ -1 +1,185 @@
1
- # fluid_cli
1
+ # Fluid CLI
2
+
3
+ The official command-line interface for the [Fluid](https://fluid.app) e-commerce platform. Build, preview, and deploy themes for your Fluid storefront directly from the terminal.
4
+
5
+ ## Installation
6
+
7
+ ### Prerequisites
8
+
9
+ - Ruby >= 3.1.0
10
+ - Bundler
11
+
12
+ ### From Source
13
+
14
+ ```sh
15
+ git clone https://github.com/fluid-commerce/fluid-cli.git
16
+ cd fluid-cli
17
+ bundle install
18
+ ```
19
+
20
+ ### As a Gem
21
+
22
+ ```sh
23
+ gem install fluid_cli
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```sh
29
+ # Authenticate with your Fluid account
30
+ fluid login
31
+
32
+ # Initialize a new theme from the base template
33
+ fluid theme init
34
+
35
+ # Start the local development server
36
+ fluid theme dev
37
+
38
+ # Push your theme to Fluid
39
+ fluid theme push
40
+ ```
41
+
42
+ ## Authentication
43
+
44
+ Fluid CLI uses OAuth2 for authentication. Running `fluid login` will open your browser and redirect you to the Fluid authentication page. Once authenticated, your session is stored locally.
45
+
46
+ ```sh
47
+ # Log in to your Fluid account
48
+ fluid login
49
+
50
+ # Log in to a specific company
51
+ fluid login --company=my-store
52
+
53
+ # Check who you're logged in as
54
+ fluid whoami
55
+
56
+ # Switch between companies
57
+ fluid switch --company=another-store
58
+
59
+ # Log out
60
+ fluid logout
61
+ ```
62
+
63
+ You can also authenticate via environment variables:
64
+
65
+ | Variable | Description |
66
+ |----------|-------------|
67
+ | `FLUID_AUTH_TOKEN` | Bearer token for API authentication |
68
+ | `FLUID_COMPANY` | Default company subdomain |
69
+
70
+ ## Commands
71
+
72
+ ### `fluid login`
73
+
74
+ Authenticate with your Fluid account via the browser.
75
+
76
+ | Flag | Description |
77
+ |------|-------------|
78
+ | `-c, --company=COMPANY` | Specify the company to log in to |
79
+
80
+ ### `fluid logout`
81
+
82
+ Clear stored authentication tokens.
83
+
84
+ ### `fluid whoami`
85
+
86
+ Display the currently authenticated company.
87
+
88
+ ### `fluid switch`
89
+
90
+ Switch between companies on your account.
91
+
92
+ | Flag | Description |
93
+ |------|-------------|
94
+ | `-c, --company=COMPANY` | Specify the company to switch to |
95
+
96
+ ### `fluid theme init`
97
+
98
+ Initialize a new theme by cloning the base template into your current directory.
99
+
100
+ | Flag | Description |
101
+ |------|-------------|
102
+ | `-u, --clone-url=URL` | Custom Git URL to clone from |
103
+
104
+ ### `fluid theme dev`
105
+
106
+ Start a local development server with hot reload. The dev server proxies requests to your Fluid storefront and injects local theme files, enabling a fast development workflow.
107
+
108
+ | Flag | Description |
109
+ |------|-------------|
110
+ | `--host=HOST` | Host to bind the dev server to |
111
+ | `--port=PORT` | Port to bind the dev server to |
112
+ | `--poll` | Use polling instead of filesystem events for file watching |
113
+ | `--live-reload=MODE` | Reload mode: `full-page` or `hot-reload` |
114
+ | `-t, --theme=THEME_ID` | Use a specific theme |
115
+ | `-f, --force` | Force create a new development theme |
116
+ | `--overwrite-json` | Overwrite remote JSON files with local versions |
117
+ | `--navigate` | Open the interactive route/resource navigator |
118
+
119
+ ### `fluid theme push`
120
+
121
+ Upload your local theme files to Fluid.
122
+
123
+ | Flag | Description |
124
+ |------|-------------|
125
+ | `-n, --nodelete` | Don't delete remote files that are missing locally |
126
+ | `-t, --theme=THEME_ID` | Push to a specific theme |
127
+ | `-u, --unpublished` | Push to a new unpublished theme |
128
+ | `-j, --json` | Include JSON settings files |
129
+ | `-a, --allow-live` | Allow pushing to the live theme |
130
+ | `-p, --publish` | Publish the theme after pushing |
131
+ | `-f, --force` | Skip confirmation prompts |
132
+
133
+ ### `fluid theme pull`
134
+
135
+ Download theme files from Fluid to your local directory.
136
+
137
+ | Flag | Description |
138
+ |------|-------------|
139
+ | `-t, --theme=THEME_ID` | Pull from a specific theme |
140
+
141
+ ### `fluid theme navigate`
142
+
143
+ Launch an interactive navigator to browse routes and resources for your storefront.
144
+
145
+ | Flag | Description |
146
+ |------|-------------|
147
+ | `--host=HOST` | Host for the navigator server |
148
+ | `--port=PORT` | Port for the navigator server |
149
+ | `-t, --theme=THEME_ID` | Theme to navigate |
150
+
151
+ ## `.fluidignore`
152
+
153
+ You can create a `.fluidignore` file in your theme's root directory to exclude files and directories from all theme operations (dev, push, pull, and file watching). The syntax is identical to `.gitignore`:
154
+
155
+ ```gitignore
156
+ # Ignore node_modules
157
+ node_modules/
158
+
159
+ # Ignore all log files
160
+ *.log
161
+
162
+ # Ignore a specific directory
163
+ tmp/
164
+
165
+ # Negate a pattern (re-include a previously ignored file)
166
+ !important.log
167
+ ```
168
+
169
+ ## Development
170
+
171
+ After cloning the repository:
172
+
173
+ ```sh
174
+ bundle install
175
+
176
+ # Run the CLI locally
177
+ bundle exec fluid
178
+
179
+ # Run tests
180
+ bundle exec rake test
181
+ ```
182
+
183
+ ## License
184
+
185
+ This project is available under the [MIT License](LICENSE.txt).
@@ -6,11 +6,14 @@ module FluidCLI
6
6
  class FileSystemListener
7
7
  include Observable
8
8
 
9
- def initialize(root:, force_poll:)
9
+ def initialize(root:, force_poll:, ignore_pattern: nil)
10
10
  @root = root
11
11
  @force_poll = force_poll
12
12
 
13
- @listener = Listen.to(@root, force_polling: @force_poll) do |updated, added, removed|
13
+ listen_opts = { force_polling: @force_poll }
14
+ listen_opts[:ignore] = ignore_pattern if ignore_pattern
15
+
16
+ @listener = Listen.to(@root, **listen_opts) do |updated, added, removed|
14
17
  changed
15
18
  notify_observers(updated, added, removed)
16
19
  end
@@ -15,7 +15,13 @@ module FluidCLI
15
15
  @ctx = ctx
16
16
  @theme = theme
17
17
  @syncer = syncer
18
- @listener = FileSystemListener.new(root: @theme.root, force_poll: poll)
18
+
19
+ ignore_pattern = @theme.fluid_ignore.to_listen_regexp
20
+ @listener = FileSystemListener.new(
21
+ root: @theme.root,
22
+ force_poll: poll,
23
+ ignore_pattern: ignore_pattern
24
+ )
19
25
 
20
26
  add_observer(self, :upload_files_when_changed)
21
27
  end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FluidCLI
4
+ module Theme
5
+ class FluidIgnore
6
+ IGNORE_FILE = ".fluidignore"
7
+
8
+ attr_reader :patterns
9
+
10
+ def initialize(root)
11
+ @root = root
12
+ @patterns = parse(ignore_file_path)
13
+ end
14
+
15
+ # Returns true if the given relative path should be ignored.
16
+ # Last matching pattern wins (supports negation with !).
17
+ def ignore?(relative_path)
18
+ relative_path = relative_path.to_s
19
+ result = false
20
+ @patterns.each do |negated, pattern|
21
+ if match?(pattern, relative_path)
22
+ result = !negated
23
+ end
24
+ end
25
+ result
26
+ end
27
+
28
+ # Returns a Regexp suitable for Listen.to(ignore: ...).
29
+ # Returns nil if there are no positive patterns.
30
+ def to_listen_regexp
31
+ positive_patterns = @patterns.reject(&:first).map(&:last)
32
+ return nil if positive_patterns.empty?
33
+
34
+ parts = positive_patterns.map { |pat| fnmatch_to_regex(pat) }
35
+ Regexp.new(parts.join("|"))
36
+ end
37
+
38
+ def any?
39
+ @patterns.any?
40
+ end
41
+
42
+ private
43
+
44
+ def ignore_file_path
45
+ ::File.join(@root.to_s, IGNORE_FILE)
46
+ end
47
+
48
+ def parse(path)
49
+ return [] unless ::File.exist?(path)
50
+
51
+ ::File.readlines(path, chomp: true).filter_map do |line|
52
+ line = line.strip
53
+ next if line.empty? || line.start_with?("#")
54
+
55
+ negated = line.start_with?("!")
56
+ line = line[1..] if negated
57
+
58
+ # Leading slash means relative to root — strip it for fnmatch
59
+ line = line[1..] if line.start_with?("/")
60
+
61
+ [negated, line]
62
+ end
63
+ end
64
+
65
+ def match?(pattern, path)
66
+ if pattern.end_with?("/")
67
+ # Directory pattern — match anything under this directory
68
+ path.start_with?(pattern) || path == pattern.chomp("/")
69
+ elsif pattern.include?("/")
70
+ # Path pattern — match against the full relative path
71
+ ::File.fnmatch(pattern, path, ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH)
72
+ else
73
+ # Basename pattern — match against both full path and basename
74
+ ::File.fnmatch(pattern, path, ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH) ||
75
+ ::File.fnmatch(pattern, ::File.basename(path), ::File::FNM_DOTMATCH)
76
+ end
77
+ end
78
+
79
+ def fnmatch_to_regex(pattern)
80
+ if pattern.end_with?("/")
81
+ # Directory pattern — match the directory and anything under it
82
+ Regexp.escape(pattern) + ".*"
83
+ else
84
+ re = Regexp.escape(pattern)
85
+ re.gsub("\\*\\*", "DOUBLE_STAR")
86
+ .gsub("\\*", "[^/]*")
87
+ .gsub("DOUBLE_STAR", ".*")
88
+ .gsub("\\?", "[^/]")
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative "file"
3
+ require_relative "fluid_ignore"
3
4
  require "pathname"
4
5
 
5
6
  module FluidCLI
@@ -24,11 +25,16 @@ module FluidCLI
24
25
  glob("**/*.json")
25
26
  end
26
27
 
28
+ def fluid_ignore
29
+ @fluid_ignore ||= FluidIgnore.new(root)
30
+ end
31
+
27
32
  def glob(pattern, raise_on_dir: false)
28
33
  root
29
34
  .glob(pattern)
30
35
  .select { |path| file?(path, raise_on_dir) }
31
36
  .map { |path| File.new(path, root) }
37
+ .reject { |file| fluid_ignore.ignore?(file.relative_path) }
32
38
  end
33
39
 
34
40
  def static_asset_file?(file)
@@ -1,3 +1,3 @@
1
1
  module FluidCLI
2
- VERSION = "0.1.9"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluid_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fluid
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-02-24 00:00:00.000000000 Z
10
+ date: 2026-03-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bugsnag
@@ -142,6 +142,7 @@ files:
142
142
  - lib/fluid_cli/theme/dev_server/web_server.rb
143
143
  - lib/fluid_cli/theme/development_theme.rb
144
144
  - lib/fluid_cli/theme/file.rb
145
+ - lib/fluid_cli/theme/fluid_ignore.rb
145
146
  - lib/fluid_cli/theme/forms/select.rb
146
147
  - lib/fluid_cli/theme/mime_type.rb
147
148
  - lib/fluid_cli/theme/navigation/resource_fetcher.rb