shakapacker 9.4.0 → 9.6.0.beta.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 +4 -4
- data/.github/workflows/claude-code-review.yml +6 -15
- data/.github/workflows/claude.yml +3 -2
- data/.prettierignore +4 -0
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +75 -2
- data/CLAUDE.md +12 -0
- data/bin/conductor-exec +24 -0
- data/bin/shakapacker-config +11 -0
- data/conductor-setup.sh +116 -39
- data/conductor.json +3 -1
- data/docs/configuration.md +51 -8
- data/docs/early_hints.md +9 -2
- data/docs/precompile_hook.md +47 -1
- data/lib/install/template.rb +8 -4
- data/lib/shakapacker/configuration.rb +1 -1
- data/lib/shakapacker/dev_server_runner.rb +1 -3
- data/lib/shakapacker/helper.rb +2 -0
- data/lib/shakapacker/runner.rb +12 -1
- data/lib/shakapacker/version.rb +1 -1
- data/lib/shakapacker.rb +11 -4
- data/package/config.ts +4 -0
- data/package/plugins/envFilter.ts +82 -0
- data/package/plugins/rspack.ts +4 -1
- data/package/plugins/webpack.ts +4 -1
- data/package/rules/file.ts +1 -1
- data/package/rules/sass.ts +1 -0
- data/package/types.ts +1 -0
- data/package.json +1 -1
- data/shakapacker.gemspec +1 -1
- data/sig/shakapacker/compiler.rbs +1 -0
- data/test/package/config.test.js +11 -0
- data/test/package/plugins/envFiltering.test.js +453 -0
- data/test/package/rules/file.test.js +4 -0
- data/test/package/rules/sass1.test.js +4 -0
- metadata +7 -3
- data/Gemfile.lock +0 -254
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16a87e8e339c085afc34f5f70af6fee7aa534590949f02faa1863d9c5e7f9f7d
|
|
4
|
+
data.tar.gz: 19d1f859e00dc29bfe4c593ba1b4a1e5b64c6347fc1b56a2cbabcc8c8020205b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f4286284b7cbbec5324ce2ad26c6d6892cfcecae96e31c7324ff0a533f332301b4008e7083592b3b4b639be131758ab6fa5bc81ed46ac0903f2370f20a05fae0
|
|
7
|
+
data.tar.gz: e4e6bfdd5837dabfd9f5d031e5a1d905ca88b6c1d50b1f3d440fb3f3c6a23d0abbec1d8839a769536af52d67eed5657ec602786c824dd57156f1678026ec300e
|
|
@@ -2,7 +2,7 @@ name: Claude Code Review
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
pull_request:
|
|
5
|
-
types: [opened, synchronize]
|
|
5
|
+
types: [opened, synchronize, ready_for_review, reopened]
|
|
6
6
|
# Optional: Only run on specific file changes
|
|
7
7
|
# paths:
|
|
8
8
|
# - "src/**/*.ts"
|
|
@@ -36,18 +36,9 @@ jobs:
|
|
|
36
36
|
uses: anthropics/claude-code-action@v1
|
|
37
37
|
with:
|
|
38
38
|
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- Potential bugs or issues
|
|
43
|
-
- Performance considerations
|
|
44
|
-
- Security concerns
|
|
45
|
-
- Test coverage
|
|
46
|
-
|
|
47
|
-
Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
|
|
48
|
-
|
|
49
|
-
Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.
|
|
50
|
-
|
|
39
|
+
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
|
|
40
|
+
plugins: 'code-review@claude-code-plugins'
|
|
41
|
+
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
|
|
51
42
|
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
52
|
-
# or https://
|
|
53
|
-
|
|
43
|
+
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
44
|
+
|
|
@@ -45,5 +45,6 @@ jobs:
|
|
|
45
45
|
|
|
46
46
|
# Optional: Add claude_args to customize behavior and configuration
|
|
47
47
|
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
48
|
-
# or https://
|
|
49
|
-
# claude_args: '--
|
|
48
|
+
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
49
|
+
# claude_args: '--allowed-tools Bash(gh pr:*)'
|
|
50
|
+
|
data/.prettierignore
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
# Template files that should maintain their original quote style
|
|
2
2
|
lib/install/config/**/*.ts
|
|
3
3
|
lib/install/config/**/*.js
|
|
4
|
+
|
|
5
|
+
# Temporarily ignore workflow files that cannot be modified in PRs due to claude-review validation
|
|
6
|
+
.github/workflows/claude.yml
|
|
7
|
+
.github/workflows/claude-code-review.yml
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -11,9 +11,80 @@
|
|
|
11
11
|
|
|
12
12
|
Changes since the last non-beta release.
|
|
13
13
|
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **BREAKING: sass-loader now defaults to modern Sass API**. [PR #879](https://github.com/shakacode/shakapacker/pull/879) by [justin808](https://github.com/justin808). The sass-loader configuration now uses `api: "modern"` instead of the deprecated legacy API. This improves compatibility with plugins like sass-resources-loader that require the modern API. If you experience issues after upgrading, you can revert to the legacy API by customizing your webpack config:
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
// config/webpack/webpack.config.js
|
|
20
|
+
const { generateWebpackConfig } = require("shakapacker")
|
|
21
|
+
const config = generateWebpackConfig()
|
|
22
|
+
|
|
23
|
+
// Find and modify sass-loader options
|
|
24
|
+
config.module.rules.forEach((rule) => {
|
|
25
|
+
if (rule.use) {
|
|
26
|
+
rule.use.forEach((loader) => {
|
|
27
|
+
if (loader.loader?.includes("sass-loader")) {
|
|
28
|
+
loader.options.api = "legacy"
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
module.exports = config
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **Fixed orphaned webpack/rspack processes when foreman receives SIGTERM**. [PR #888](https://github.com/shakacode/shakapacker/pull/888) by [jordan-brough](https://github.com/jordan-brough). When running under foreman, sending SIGTERM to foreman (e.g. `kill <pid>`) would kill the Ruby shakapacker process but leave the webpack/rspack child process running as an orphan. DevServerRunner now uses `exec` to replace the Ruby process entirely, and Runner uses `spawn` with SIGTERM forwarding to ensure the child process is properly terminated.
|
|
40
|
+
- **Fixed installer writing wrong shakapacker version in package.json**. [PR #899](https://github.com/shakacode/shakapacker/pull/899) by [justin808](https://github.com/justin808). The `shakapacker:install` generator now keeps the `package.json` dependency value in sync with the exact version or path that was requested, instead of relying on the post-install value which could differ.
|
|
41
|
+
- **Fixed NODE_ENV=test causing DefinePlugin warnings**. [PR #870](https://github.com/shakacode/shakapacker/pull/870) by [justin808](https://github.com/justin808). When RAILS_ENV=test, Shakapacker now sets NODE_ENV=development instead of NODE_ENV=test. This prevents webpack/rspack DefinePlugin conflicts since these bundlers only recognize "development" and "production" as valid NODE_ENV values.
|
|
42
|
+
|
|
43
|
+
### Documentation
|
|
44
|
+
|
|
45
|
+
- **Added CDN limitation warnings for Early Hints feature**. [PR #878](https://github.com/shakacode/shakapacker/pull/878) by [justin808](https://github.com/justin808). The early hints documentation now prominently notes that most CDNs (Cloudflare, AWS CloudFront, AWS ALB) strip HTTP 103 responses before they reach end users. Debug mode also includes CDN warnings in HTML comments.
|
|
46
|
+
|
|
47
|
+
## [v9.5.0] - January 7, 2026
|
|
48
|
+
|
|
49
|
+
### Security
|
|
50
|
+
|
|
51
|
+
- **CRITICAL: Fixed environment variable leak via EnvironmentPlugin**. [PR #857](https://github.com/shakacode/shakapacker/pull/857) by [justin808](https://github.com/justin808). The default webpack and rspack plugins were passing the entire `process.env` to `EnvironmentPlugin`, which exposed ALL build environment variables (including secrets like `DATABASE_URL`, `AWS_SECRET_ACCESS_KEY`, `RAILS_MASTER_KEY`, etc.) to client-side JavaScript bundles when code referenced `process.env.VARIABLE_NAME`. **Note**: This issue is especially critical with webpack 5.103+ due to a [serialization change](https://github.com/webpack/webpack/commit/eecdeeb746b2f996ed4ab74365dd72c95070196b) that can embed all environment variables into bundles when `import.meta.env` is accessed conditionally. This vulnerability was inherited from webpacker v1.0.0 (January 2017) and has been present in all versions of webpacker and shakapacker. **Action required**: After upgrading, rotate any secrets that may have been exposed in production JavaScript bundles.
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- **Added `SHAKAPACKER_PUBLIC_*` prefix convention for client-side environment variables**. [PR #857](https://github.com/shakacode/shakapacker/pull/857) by [justin808](https://github.com/justin808). Any environment variable prefixed with `SHAKAPACKER_PUBLIC_` is automatically exposed to client-side JavaScript. This follows the same convention used by Next.js (`NEXT_PUBLIC_*`) and Vite (`VITE_*`), making it explicit which variables are intended for client-side use.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# These are automatically available in your JavaScript
|
|
59
|
+
export SHAKAPACKER_PUBLIC_API_URL=https://api.example.com
|
|
60
|
+
export SHAKAPACKER_PUBLIC_ANALYTICS_ID=UA-12345
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- **Added `SHAKAPACKER_ENV_VARS` environment variable as escape hatch for extending allowed client-side env vars**. [PR #857](https://github.com/shakacode/shakapacker/pull/857) by [justin808](https://github.com/justin808). Set `SHAKAPACKER_ENV_VARS=VAR1,VAR2,VAR3` to expose additional environment variables to client-side JavaScript beyond the default allowlist (`NODE_ENV`, `RAILS_ENV`, `WEBPACK_SERVE`). Only add non-sensitive variables that are safe to embed in public JavaScript bundles.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- **BREAKING: EnvironmentPlugin now uses allowlist instead of exposing all env vars**. [PR #857](https://github.com/shakacode/shakapacker/pull/857) by [justin808](https://github.com/justin808). Only `NODE_ENV`, `RAILS_ENV`, `WEBPACK_SERVE`, and any `SHAKAPACKER_PUBLIC_*` variables are exposed by default. If your client-side code relies on other environment variables, either rename them with the `SHAKAPACKER_PUBLIC_` prefix (recommended), add them via `SHAKAPACKER_ENV_VARS`, or customize your webpack/rspack config. This is a security fix - the previous behavior was dangerous.
|
|
68
|
+
|
|
69
|
+
**Migration examples:**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Option 1 (recommended): Use the SHAKAPACKER_PUBLIC_ prefix
|
|
73
|
+
export SHAKAPACKER_PUBLIC_API_BASE_URL=https://api.example.com
|
|
74
|
+
|
|
75
|
+
# Option 2: Use SHAKAPACKER_ENV_VARS for existing variable names
|
|
76
|
+
SHAKAPACKER_ENV_VARS=API_BASE_URL bundle exec rails assets:precompile
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Fixed
|
|
80
|
+
|
|
81
|
+
- **Fixed gemspec to exclude Gemfile.lock from published gem**. [PR #856](https://github.com/shakacode/shakapacker/pull/856) by [adrien-k](https://github.com/adrien-k). The gemspec's file pattern now correctly excludes `Gemfile.lock`, preventing vulnerability alerts during Docker image scans caused by outdated pinned versions in the lock file.
|
|
82
|
+
|
|
83
|
+
## [v9.4.0] - November 22, 2025
|
|
84
|
+
|
|
14
85
|
### Added
|
|
15
86
|
|
|
16
|
-
- **Added `SHAKAPACKER_SKIP_PRECOMPILE_HOOK` environment variable to skip precompile hook**. [PR #
|
|
87
|
+
- **Added `SHAKAPACKER_SKIP_PRECOMPILE_HOOK` environment variable to skip precompile hook**. [PR #850](https://github.com/shakacode/shakapacker/pull/850) by [justin808](https://github.com/justin808). Set `SHAKAPACKER_SKIP_PRECOMPILE_HOOK=true` to skip the precompile hook during compilation. This is useful when using process managers like Foreman or Overmind to run the hook once before starting multiple webpack processes, preventing duplicate hook execution. **Migration tip:** If you have a custom `bin/dev` script that starts multiple webpack processes, you can now run the precompile hook once in the script and set this environment variable to prevent each webpack process from running the hook again. See the [precompile hook documentation](./docs/precompile_hook.md#skipping-the-hook) for implementation examples.
|
|
17
88
|
|
|
18
89
|
## [v9.3.4-beta.0] - November 17, 2025
|
|
19
90
|
|
|
@@ -763,7 +834,9 @@ Note: [Rubygem is 6.3.0.pre.rc.1](https://rubygems.org/gems/shakapacker/versions
|
|
|
763
834
|
|
|
764
835
|
See [CHANGELOG.md in rails/webpacker (up to v5.4.3)](https://github.com/rails/webpacker/blob/master/CHANGELOG.md)
|
|
765
836
|
|
|
766
|
-
[Unreleased]: https://github.com/shakacode/shakapacker/compare/v9.
|
|
837
|
+
[Unreleased]: https://github.com/shakacode/shakapacker/compare/v9.5.0...main
|
|
838
|
+
[v9.5.0]: https://github.com/shakacode/shakapacker/compare/v9.4.0...v9.5.0
|
|
839
|
+
[v9.4.0]: https://github.com/shakacode/shakapacker/compare/v9.3.4...v9.4.0
|
|
767
840
|
[v9.3.4-beta.0]: https://github.com/shakacode/shakapacker/compare/v9.3.3...v9.3.4-beta.0
|
|
768
841
|
[v9.3.3]: https://github.com/shakacode/shakapacker/compare/v9.3.2...v9.3.3
|
|
769
842
|
[v9.3.2]: https://github.com/shakacode/shakapacker/compare/v9.3.1...v9.3.2
|
data/CLAUDE.md
CHANGED
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
- Run corresponding RSpec tests when changing source files
|
|
13
13
|
- For example, when changing `lib/shakapacker/foo.rb`, run `spec/shakapacker/foo_spec.rb`
|
|
14
14
|
- Run the full test suite with `bundle exec rspec` before pushing
|
|
15
|
+
- **Use explicit RSpec spy assertions** - prefer `have_received`/`not_to have_received` over indirect counter patterns
|
|
16
|
+
- Good: `expect(Open3).to have_received(:capture3).with(anything, hook_command, anything)`
|
|
17
|
+
- Good: `expect(Open3).not_to have_received(:capture3).with(anything, hook_command, anything)`
|
|
18
|
+
- Avoid: `call_count += 1` followed by `expect(call_count).to eq(1)`
|
|
15
19
|
|
|
16
20
|
## Code Style
|
|
17
21
|
|
|
@@ -41,3 +45,11 @@
|
|
|
41
45
|
- This gem supports both webpack and rspack configurations
|
|
42
46
|
- Test changes with both bundlers when modifying core functionality
|
|
43
47
|
- Be aware of the dual package.json/Gemfile dependency management
|
|
48
|
+
|
|
49
|
+
## Conductor Environment
|
|
50
|
+
|
|
51
|
+
- **Version manager support**: The setup script detects mise, asdf, or direct PATH tools (rbenv/nvm/nodenv)
|
|
52
|
+
- **bin/conductor-exec**: Use this wrapper for commands when tool versions aren't detected correctly in Conductor's non-interactive shell
|
|
53
|
+
- Example: `bin/conductor-exec bundle exec rubocop`
|
|
54
|
+
- The wrapper uses `mise exec` if mise is available, otherwise falls back to direct execution
|
|
55
|
+
- **conductor.json scripts** already use this wrapper, so you typically don't need to use it manually
|
data/bin/conductor-exec
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/zsh
|
|
2
|
+
# Wrapper script to run commands in Conductor with proper tool versions
|
|
3
|
+
# Usage: bin/conductor-exec <command> [args...]
|
|
4
|
+
#
|
|
5
|
+
# This is needed because Conductor (and other non-interactive shells) don't
|
|
6
|
+
# source .zshrc, so version manager PATH configuration isn't active by default.
|
|
7
|
+
#
|
|
8
|
+
# - If mise is available: uses `mise exec` for correct tool versions
|
|
9
|
+
# - Otherwise: falls back to direct execution (for asdf/rbenv/nvm users)
|
|
10
|
+
#
|
|
11
|
+
# Examples:
|
|
12
|
+
# bin/conductor-exec ruby --version # Uses correct Ruby version
|
|
13
|
+
# bin/conductor-exec bundle exec rubocop # Correct Ruby for linting
|
|
14
|
+
# bin/conductor-exec git commit -m "msg" # Pre-commit hooks work correctly
|
|
15
|
+
# bin/conductor-exec yarn install # Uses correct Node version
|
|
16
|
+
#
|
|
17
|
+
# See: https://github.com/shakacode/react_on_rails-demos/issues/105
|
|
18
|
+
|
|
19
|
+
if command -v mise &> /dev/null; then
|
|
20
|
+
exec mise exec -- "$@"
|
|
21
|
+
else
|
|
22
|
+
# Fall back to direct execution for non-mise users
|
|
23
|
+
exec "$@"
|
|
24
|
+
fi
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Minimal shim - all logic is in the TypeScript module
|
|
4
|
+
const { run } = require("shakapacker/configExporter")
|
|
5
|
+
|
|
6
|
+
run(process.argv.slice(2))
|
|
7
|
+
.then((exitCode) => process.exit(exitCode))
|
|
8
|
+
.catch((error) => {
|
|
9
|
+
console.error(error.message)
|
|
10
|
+
process.exit(1)
|
|
11
|
+
})
|
data/conductor-setup.sh
CHANGED
|
@@ -1,43 +1,124 @@
|
|
|
1
|
-
#!/
|
|
1
|
+
#!/bin/zsh
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
echo "🔧 Setting up Shakapacker workspace..."
|
|
5
5
|
|
|
6
|
-
#
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
# Detect and initialize version manager
|
|
7
|
+
# Supports: mise, asdf, or direct PATH (rbenv/nvm/nodenv already in PATH)
|
|
8
|
+
VERSION_MANAGER="none"
|
|
9
|
+
|
|
10
|
+
echo "📋 Detecting version manager..."
|
|
11
|
+
|
|
12
|
+
if command -v mise &> /dev/null; then
|
|
13
|
+
VERSION_MANAGER="mise"
|
|
14
|
+
echo "✅ Found mise"
|
|
15
|
+
# Trust mise config for current directory only
|
|
16
|
+
mise trust 2>/dev/null || true
|
|
17
|
+
elif [[ -f ~/.asdf/asdf.sh ]]; then
|
|
18
|
+
VERSION_MANAGER="asdf"
|
|
19
|
+
source ~/.asdf/asdf.sh
|
|
20
|
+
echo "✅ Found asdf (from ~/.asdf/asdf.sh)"
|
|
21
|
+
elif command -v asdf &> /dev/null; then
|
|
22
|
+
VERSION_MANAGER="asdf"
|
|
23
|
+
# For homebrew-installed asdf
|
|
24
|
+
if [[ -f /opt/homebrew/opt/asdf/libexec/asdf.sh ]]; then
|
|
25
|
+
source /opt/homebrew/opt/asdf/libexec/asdf.sh
|
|
26
|
+
fi
|
|
27
|
+
echo "✅ Found asdf"
|
|
13
28
|
else
|
|
14
|
-
|
|
29
|
+
echo "ℹ️ No version manager detected, using system PATH"
|
|
30
|
+
echo " (Assuming rbenv/nvm/nodenv or system tools are already configured)"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Ensure version config exists for asdf/mise users
|
|
34
|
+
if [[ "$VERSION_MANAGER" != "none" ]] && [[ ! -f .tool-versions ]] && [[ ! -f .mise.toml ]]; then
|
|
35
|
+
echo "📝 Creating .tool-versions from project version files..."
|
|
36
|
+
|
|
37
|
+
# Read Ruby version from .ruby-version or use default
|
|
38
|
+
if [[ -f .ruby-version ]]; then
|
|
39
|
+
RUBY_VER=$(cat .ruby-version | tr -d '[:space:]')
|
|
40
|
+
else
|
|
41
|
+
RUBY_VER="3.3.4" # Default: recent stable Ruby
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# Read Node version from .node-version or use default
|
|
45
|
+
if [[ -f .node-version ]]; then
|
|
46
|
+
NODE_VER=$(cat .node-version | tr -d '[:space:]')
|
|
47
|
+
else
|
|
48
|
+
NODE_VER="20.18.0" # Default: LTS Node
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
cat > .tool-versions << EOF
|
|
52
|
+
ruby $RUBY_VER
|
|
53
|
+
nodejs $NODE_VER
|
|
54
|
+
EOF
|
|
55
|
+
echo " Using Ruby $RUBY_VER, Node $NODE_VER"
|
|
15
56
|
fi
|
|
16
57
|
|
|
17
|
-
#
|
|
18
|
-
if
|
|
19
|
-
echo "
|
|
20
|
-
|
|
58
|
+
# Install tools via mise (after .tool-versions exists)
|
|
59
|
+
if [[ "$VERSION_MANAGER" == "mise" ]]; then
|
|
60
|
+
echo "📦 Installing tools via mise..."
|
|
61
|
+
mise install
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Helper function to run commands with the detected version manager
|
|
65
|
+
run_cmd() {
|
|
66
|
+
if [[ "$VERSION_MANAGER" == "mise" ]] && [[ -x "bin/conductor-exec" ]]; then
|
|
67
|
+
bin/conductor-exec "$@"
|
|
68
|
+
else
|
|
69
|
+
"$@"
|
|
70
|
+
fi
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Check required tools
|
|
74
|
+
echo "📋 Checking required tools..."
|
|
75
|
+
run_cmd ruby --version >/dev/null 2>&1 || { echo "❌ Error: Ruby is not installed or not in PATH."; exit 1; }
|
|
76
|
+
run_cmd node --version >/dev/null 2>&1 || { echo "❌ Error: Node.js is not installed or not in PATH."; exit 1; }
|
|
77
|
+
|
|
78
|
+
# Check Ruby version
|
|
79
|
+
RUBY_VERSION=$(run_cmd ruby -v | awk '{print $2}')
|
|
80
|
+
MIN_RUBY_VERSION="2.7.0"
|
|
81
|
+
if [[ $(echo -e "$MIN_RUBY_VERSION\n$RUBY_VERSION" | sort -V | head -n1) != "$MIN_RUBY_VERSION" ]]; then
|
|
82
|
+
echo "❌ Error: Ruby version $RUBY_VERSION is too old. Shakapacker requires Ruby >= 2.7.0"
|
|
83
|
+
echo " Please upgrade Ruby using your version manager or system package manager."
|
|
21
84
|
exit 1
|
|
22
85
|
fi
|
|
86
|
+
echo "✅ Ruby version: $RUBY_VERSION"
|
|
23
87
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
88
|
+
# Check Node version
|
|
89
|
+
NODE_VERSION=$(run_cmd node -v | cut -d'v' -f2)
|
|
90
|
+
MIN_NODE_VERSION="14.0.0"
|
|
91
|
+
if [[ $(echo -e "$MIN_NODE_VERSION\n$NODE_VERSION" | sort -V | head -n1) != "$MIN_NODE_VERSION" ]]; then
|
|
92
|
+
echo "❌ Error: Node.js version v$NODE_VERSION is too old. Shakapacker requires Node.js >= 14.0.0"
|
|
93
|
+
echo " Please upgrade Node.js using your version manager or system package manager."
|
|
27
94
|
exit 1
|
|
28
95
|
fi
|
|
96
|
+
echo "✅ Node.js version: v$NODE_VERSION"
|
|
97
|
+
|
|
98
|
+
# Copy any environment files from root if they exist
|
|
99
|
+
if [ -n "${CONDUCTOR_ROOT_PATH:-}" ]; then
|
|
100
|
+
if [ -f "$CONDUCTOR_ROOT_PATH/.env" ]; then
|
|
101
|
+
echo "📝 Copying .env file..."
|
|
102
|
+
cp "$CONDUCTOR_ROOT_PATH/.env" .env
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
if [ -f "$CONDUCTOR_ROOT_PATH/.env.local" ]; then
|
|
106
|
+
echo "📝 Copying .env.local file..."
|
|
107
|
+
cp "$CONDUCTOR_ROOT_PATH/.env.local" .env.local
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
29
110
|
|
|
30
111
|
# Install Ruby dependencies
|
|
31
|
-
echo "
|
|
32
|
-
|
|
112
|
+
echo "💎 Installing Ruby dependencies..."
|
|
113
|
+
run_cmd bundle install
|
|
33
114
|
|
|
34
115
|
# Install JavaScript dependencies
|
|
35
116
|
echo "📦 Installing JavaScript dependencies..."
|
|
36
|
-
yarn install --frozen-lockfile
|
|
117
|
+
run_cmd yarn install --frozen-lockfile
|
|
37
118
|
|
|
38
119
|
# Set up Husky git hooks
|
|
39
120
|
echo "🪝 Setting up Husky git hooks..."
|
|
40
|
-
npx husky
|
|
121
|
+
run_cmd npx husky
|
|
41
122
|
if [ ! -f .husky/pre-commit ]; then
|
|
42
123
|
echo "Creating pre-commit hook..."
|
|
43
124
|
cat > .husky/pre-commit << 'EOF'
|
|
@@ -47,24 +128,20 @@ EOF
|
|
|
47
128
|
chmod +x .husky/pre-commit
|
|
48
129
|
fi
|
|
49
130
|
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
echo "📋 Copying .env file from root..."
|
|
54
|
-
cp "$CONDUCTOR_ROOT_PATH/.env" .env
|
|
55
|
-
fi
|
|
56
|
-
|
|
57
|
-
if [ -f "$CONDUCTOR_ROOT_PATH/.env.local" ]; then
|
|
58
|
-
echo "📋 Copying .env.local file from root..."
|
|
59
|
-
cp "$CONDUCTOR_ROOT_PATH/.env.local" .env.local
|
|
60
|
-
fi
|
|
61
|
-
fi
|
|
131
|
+
# Verify linting tools are available
|
|
132
|
+
echo "✅ Verifying linting tools..."
|
|
133
|
+
run_cmd bundle exec rubocop --version
|
|
62
134
|
|
|
63
|
-
echo "
|
|
135
|
+
echo "✨ Workspace setup complete!"
|
|
64
136
|
echo ""
|
|
65
|
-
echo "
|
|
66
|
-
echo "
|
|
67
|
-
echo "
|
|
68
|
-
echo " - Run JavaScript tests
|
|
69
|
-
echo " -
|
|
70
|
-
echo " -
|
|
137
|
+
echo "📚 Key commands:"
|
|
138
|
+
echo " • bundle exec rspec - Run Ruby tests"
|
|
139
|
+
echo " • bundle exec rake run_spec:gem - Run gem-specific tests"
|
|
140
|
+
echo " • yarn test - Run JavaScript tests"
|
|
141
|
+
echo " • yarn lint - Run JavaScript linting"
|
|
142
|
+
echo " • bundle exec rubocop - Run Ruby linting (required before commits)"
|
|
143
|
+
echo ""
|
|
144
|
+
if [[ "$VERSION_MANAGER" == "mise" ]]; then
|
|
145
|
+
echo "💡 Tip: Use 'bin/conductor-exec <command>' if tool versions aren't detected correctly."
|
|
146
|
+
fi
|
|
147
|
+
echo "⚠️ Remember: Always run 'bundle exec rubocop' before committing!"
|
data/conductor.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"scripts": {
|
|
3
3
|
"setup": "./conductor-setup.sh",
|
|
4
|
-
"run": "bundle exec rspec",
|
|
4
|
+
"run": "bin/conductor-exec bundle exec rspec",
|
|
5
|
+
"test": "bin/conductor-exec bundle exec rspec",
|
|
6
|
+
"lint": "bin/conductor-exec bundle exec rubocop && bin/conductor-exec yarn lint",
|
|
5
7
|
"archive": ""
|
|
6
8
|
}
|
|
7
9
|
}
|
data/docs/configuration.md
CHANGED
|
@@ -617,14 +617,57 @@ Shakapacker validates configuration at runtime and provides helpful error messag
|
|
|
617
617
|
|
|
618
618
|
Some options can be overridden via environment variables:
|
|
619
619
|
|
|
620
|
-
| Variable | Description
|
|
621
|
-
| ---------------------------- |
|
|
622
|
-
| `SHAKAPACKER_CONFIG` | Path to shakapacker.yml
|
|
623
|
-
| `SHAKAPACKER_ASSETS_BUNDLER` | Override assets bundler
|
|
624
|
-
| `SHAKAPACKER_PRECOMPILE` | Override precompile flag
|
|
625
|
-
| `SHAKAPACKER_ASSET_HOST` | Override asset host
|
|
626
|
-
| `
|
|
627
|
-
| `
|
|
620
|
+
| Variable | Description | Example |
|
|
621
|
+
| ---------------------------- | -------------------------------------------------- | ---------------------------- |
|
|
622
|
+
| `SHAKAPACKER_CONFIG` | Path to shakapacker.yml | `config/webpack.yml` |
|
|
623
|
+
| `SHAKAPACKER_ASSETS_BUNDLER` | Override assets bundler | `rspack` |
|
|
624
|
+
| `SHAKAPACKER_PRECOMPILE` | Override precompile flag | `false` |
|
|
625
|
+
| `SHAKAPACKER_ASSET_HOST` | Override asset host | `https://cdn.example.com` |
|
|
626
|
+
| `SHAKAPACKER_PUBLIC_*` | Auto-exposed to client-side JS (prefix convention) | `SHAKAPACKER_PUBLIC_API_URL` |
|
|
627
|
+
| `SHAKAPACKER_ENV_VARS` | Additional env vars to expose to client-side JS | `API_URL,FEATURE_FLAGS` |
|
|
628
|
+
| `NODE_ENV` | Node environment | `production` |
|
|
629
|
+
| `RAILS_ENV` | Rails environment | `staging` |
|
|
630
|
+
|
|
631
|
+
### Exposing Environment Variables to Client-Side JavaScript
|
|
632
|
+
|
|
633
|
+
By default, only `NODE_ENV`, `RAILS_ENV`, and `WEBPACK_SERVE` are exposed to client-side JavaScript via webpack/rspack's `EnvironmentPlugin`. This is a security measure to prevent accidentally leaking secrets like `DATABASE_URL`, `API_SECRET_KEY`, etc. into your JavaScript bundles.
|
|
634
|
+
|
|
635
|
+
#### SHAKAPACKER*PUBLIC*\* Prefix (Recommended)
|
|
636
|
+
|
|
637
|
+
Any environment variable prefixed with `SHAKAPACKER_PUBLIC_` is automatically exposed to client-side code. This follows the same convention used by Next.js (`NEXT_PUBLIC_*`) and Vite (`VITE_*`):
|
|
638
|
+
|
|
639
|
+
```bash
|
|
640
|
+
# These are automatically available in your JavaScript
|
|
641
|
+
export SHAKAPACKER_PUBLIC_API_URL=https://api.example.com
|
|
642
|
+
export SHAKAPACKER_PUBLIC_ANALYTICS_ID=UA-12345
|
|
643
|
+
export SHAKAPACKER_PUBLIC_FEATURE_FLAGS=dark_mode,beta_ui
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
```javascript
|
|
647
|
+
// Access in your JavaScript code
|
|
648
|
+
console.log(process.env.SHAKAPACKER_PUBLIC_API_URL)
|
|
649
|
+
console.log(process.env.SHAKAPACKER_PUBLIC_ANALYTICS_ID)
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
The prefix makes it explicit which variables are intended for client-side use, preventing accidental exposure of secrets.
|
|
653
|
+
|
|
654
|
+
#### SHAKAPACKER_ENV_VARS (Legacy/Escape Hatch)
|
|
655
|
+
|
|
656
|
+
For variables without the `SHAKAPACKER_PUBLIC_` prefix, you can use `SHAKAPACKER_ENV_VARS` to expose them:
|
|
657
|
+
|
|
658
|
+
```bash
|
|
659
|
+
# Expose additional variables during build
|
|
660
|
+
SHAKAPACKER_ENV_VARS=API_BASE_URL,FEATURE_FLAGS bundle exec rake assets:precompile
|
|
661
|
+
|
|
662
|
+
# In development
|
|
663
|
+
SHAKAPACKER_ENV_VARS=API_BASE_URL bin/shakapacker-dev-server
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
```javascript
|
|
667
|
+
// These are available after adding to SHAKAPACKER_ENV_VARS
|
|
668
|
+
console.log(process.env.API_BASE_URL)
|
|
669
|
+
console.log(process.env.FEATURE_FLAGS)
|
|
670
|
+
```
|
|
628
671
|
|
|
629
672
|
## Best Practices
|
|
630
673
|
|
data/docs/early_hints.md
CHANGED
|
@@ -4,6 +4,8 @@ This guide shows you how to use HTTP 103 Early Hints with Shakapacker to optimiz
|
|
|
4
4
|
|
|
5
5
|
> **📚 Related Documentation:** For advanced manual control using `send_pack_early_hints` in controllers before expensive work, see [early_hints_manual_api.md](early_hints_manual_api.md).
|
|
6
6
|
|
|
7
|
+
> ⚠️ **CDN Limitation**: Most CDNs (Cloudflare, AWS CloudFront, AWS ALB, etc.) strip HTTP 103 Early Hints before they reach end users. While Shakapacker sends early hints correctly, and your application server (Puma/Thruster) forwards them properly, **CDNs typically strip the 103 response**. End users will only receive the 200 response with Link headers (which arrive too late to provide early hints benefits). This is an industry-wide limitation—even major sites like GitHub, Google, and Shopify don't serve 103 in production through CDNs. For full early hints delivery, you need either direct connections without a CDN, or specific CDN configuration (e.g., Cloudflare's Early Hints feature on paid plans, which works differently by caching Link headers). See [Reverse proxy stripping 103 responses](#reverse-proxy-stripping-103-responses) for configuration details.
|
|
8
|
+
|
|
7
9
|
## What are Early Hints?
|
|
8
10
|
|
|
9
11
|
HTTP 103 Early Hints is emitted **after** Rails has finished rendering but **before** the final response is sent, allowing browsers to begin fetching resources (JS, CSS) prior to receiving the full HTML response. This may significantly improve page load performance or cause an equally significant regression, depending on the page's content.
|
|
@@ -336,8 +338,12 @@ See the [Manual API Guide](early_hints_manual_api.md) for detailed examples and
|
|
|
336
338
|
- **Modern browsers:**
|
|
337
339
|
- Chrome/Edge/Firefox 103+ ✅
|
|
338
340
|
- Safari 16.4+ ✅
|
|
341
|
+
- **Infrastructure that preserves 103 responses:**
|
|
342
|
+
- Direct connections (no CDN) ✅
|
|
343
|
+
- Cloudflare with Early Hints enabled (paid plans, works via Link header caching) ✅
|
|
344
|
+
- Most CDNs/load balancers ❌ (strip 103 responses - see [CDN Limitation](#troubleshooting) above)
|
|
339
345
|
|
|
340
|
-
If requirements not met, feature gracefully degrades with no errors.
|
|
346
|
+
If requirements not met, feature gracefully degrades with no errors. The Link headers will still be present in the 200 response, which may provide some browser prefetching benefits even when 103 is stripped.
|
|
341
347
|
|
|
342
348
|
## Quick Reference
|
|
343
349
|
|
|
@@ -377,8 +383,9 @@ Debug mode adds HTML comments to your page showing:
|
|
|
377
383
|
- What pack names were processed
|
|
378
384
|
- What Link headers were sent
|
|
379
385
|
- HTTP/2 support status
|
|
386
|
+
- CDN warning reminder (since most CDNs strip 103 responses)
|
|
380
387
|
|
|
381
|
-
View page source and look for `<!-- Shakapacker Early Hints
|
|
388
|
+
View page source and look for `<!-- Shakapacker Early Hints -->` comments.
|
|
382
389
|
|
|
383
390
|
**Early hints not appearing:**
|
|
384
391
|
|
data/docs/precompile_hook.md
CHANGED
|
@@ -23,6 +23,52 @@ The precompile hook is especially useful when you need to run commands like:
|
|
|
23
23
|
- **Development**: Runs before `bin/shakapacker --watch` or dev server starts
|
|
24
24
|
- **Production**: Runs before `bundle exec rake assets:precompile`
|
|
25
25
|
|
|
26
|
+
## Choosing an Approach
|
|
27
|
+
|
|
28
|
+
Use `precompile_hook` when your setup should always run preparatory commands right
|
|
29
|
+
before Shakapacker compiles. For React on Rails projects, this is often the
|
|
30
|
+
simplest default.
|
|
31
|
+
|
|
32
|
+
For projects with more custom startup needs (for example, additional build steps
|
|
33
|
+
or strict process ordering in `bin/dev`), you can run those commands explicitly
|
|
34
|
+
before launching long-running processes instead of using `precompile_hook`.
|
|
35
|
+
|
|
36
|
+
### Comparison
|
|
37
|
+
|
|
38
|
+
| Aspect | `precompile_hook` | Explicit setup in `bin/dev` |
|
|
39
|
+
|--------|-------------------|-------------------------------|
|
|
40
|
+
| Best for | Default/consistent pre-build tasks | Custom multi-step dev boot flows |
|
|
41
|
+
| Runs when | Immediately before compilation starts | Wherever you place it in startup script |
|
|
42
|
+
| Production integration | Automatic via `assets:precompile` | Requires explicit production wiring |
|
|
43
|
+
| Process manager complexity | Lower | Higher (you own orchestration) |
|
|
44
|
+
| Debugging | Centralized hook command | Fully explicit command-by-command flow |
|
|
45
|
+
|
|
46
|
+
### `shakapacker_precompile` Interaction
|
|
47
|
+
|
|
48
|
+
`shakapacker_precompile` controls whether Shakapacker compilation is included in
|
|
49
|
+
`assets:precompile`, while `precompile_hook` controls whether a preparatory command
|
|
50
|
+
runs before compilation.
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
# Option A: Default behavior
|
|
54
|
+
shakapacker_precompile: true
|
|
55
|
+
precompile_hook: "bin/shakapacker-precompile-hook"
|
|
56
|
+
|
|
57
|
+
# Option B: You manage compilation elsewhere
|
|
58
|
+
shakapacker_precompile: false
|
|
59
|
+
precompile_hook: "bin/shakapacker-precompile-hook"
|
|
60
|
+
|
|
61
|
+
# Option C: Fully explicit startup flow (no hook)
|
|
62
|
+
shakapacker_precompile: false
|
|
63
|
+
# precompile_hook: not set
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
To temporarily skip only the hook, set:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
SHAKAPACKER_SKIP_PRECOMPILE_HOOK=true
|
|
70
|
+
```
|
|
71
|
+
|
|
26
72
|
## Configuration
|
|
27
73
|
|
|
28
74
|
Add the `precompile_hook` option to your `config/shakapacker.yml`:
|
|
@@ -300,7 +346,7 @@ This is useful when:
|
|
|
300
346
|
- Running the hook manually and then compiling multiple times
|
|
301
347
|
- Debugging compilation issues without the hook
|
|
302
348
|
|
|
303
|
-
**Note:** The examples below show how to implement this in your custom `bin/dev` script. If you're using React on Rails
|
|
349
|
+
**Note:** The examples below show how to implement this in your custom `bin/dev` script. If you're using React on Rails v13.1.0+, the generated `bin/dev` script already implements this pattern automatically - **no action needed**. It runs the precompile hook once before launching processes, then sets `SHAKAPACKER_SKIP_PRECOMPILE_HOOK=true` to prevent duplicate execution.
|
|
304
350
|
|
|
305
351
|
**Recommended: Use Procfile env prefix**
|
|
306
352
|
|
data/lib/install/template.rb
CHANGED
|
@@ -141,6 +141,9 @@ if (setup_path = Rails.root.join("bin/setup")).exist?
|
|
|
141
141
|
end
|
|
142
142
|
|
|
143
143
|
Dir.chdir(Rails.root) do
|
|
144
|
+
npm_version = Shakapacker::Utils::VersionSyntaxConverter.new.rubygem_to_npm(Shakapacker::VERSION)
|
|
145
|
+
shakapacker_dependency_value = nil
|
|
146
|
+
|
|
144
147
|
# In CI, use the pre-packed tarball if available
|
|
145
148
|
if ENV["SHAKAPACKER_NPM_PACKAGE"]
|
|
146
149
|
package_path = ENV["SHAKAPACKER_NPM_PACKAGE"]
|
|
@@ -167,6 +170,7 @@ Dir.chdir(Rails.root) do
|
|
|
167
170
|
say "📦 Installing shakapacker from local package: #{absolute_path}", :cyan
|
|
168
171
|
begin
|
|
169
172
|
@package_json.manager.add!([absolute_path], type: :production)
|
|
173
|
+
shakapacker_dependency_value = absolute_path
|
|
170
174
|
rescue PackageJson::Error
|
|
171
175
|
say "Shakapacker installation failed 😭 See above for details.", :red
|
|
172
176
|
exit 1
|
|
@@ -174,9 +178,9 @@ Dir.chdir(Rails.root) do
|
|
|
174
178
|
else
|
|
175
179
|
say "⚠️ SHAKAPACKER_NPM_PACKAGE set but file not found: #{absolute_path}", :yellow
|
|
176
180
|
say "Falling back to npm registry...", :yellow
|
|
177
|
-
npm_version = Shakapacker::Utils::VersionSyntaxConverter.new.rubygem_to_npm(Shakapacker::VERSION)
|
|
178
181
|
begin
|
|
179
182
|
@package_json.manager.add!(["shakapacker@#{npm_version}"], type: :production)
|
|
183
|
+
shakapacker_dependency_value = npm_version
|
|
180
184
|
rescue PackageJson::Error
|
|
181
185
|
say "Shakapacker installation failed 😭 See above for details.", :red
|
|
182
186
|
exit 1
|
|
@@ -187,10 +191,10 @@ Dir.chdir(Rails.root) do
|
|
|
187
191
|
exit 1
|
|
188
192
|
end
|
|
189
193
|
else
|
|
190
|
-
npm_version = Shakapacker::Utils::VersionSyntaxConverter.new.rubygem_to_npm(Shakapacker::VERSION)
|
|
191
194
|
say "Installing shakapacker@#{npm_version}"
|
|
192
195
|
begin
|
|
193
196
|
@package_json.manager.add!(["shakapacker@#{npm_version}"], type: :production)
|
|
197
|
+
shakapacker_dependency_value = npm_version
|
|
194
198
|
rescue PackageJson::Error
|
|
195
199
|
say "Shakapacker installation failed 😭 See above for details.", :red
|
|
196
200
|
exit 1
|
|
@@ -201,8 +205,8 @@ Dir.chdir(Rails.root) do
|
|
|
201
205
|
if pj["dependencies"] && pj["dependencies"]["shakapacker"]
|
|
202
206
|
{
|
|
203
207
|
"dependencies" => pj["dependencies"].merge({
|
|
204
|
-
#
|
|
205
|
-
"shakapacker" => pj["dependencies"]["shakapacker"].delete_prefix("^")
|
|
208
|
+
# Keep package.json aligned with the exact source/version this installer requested.
|
|
209
|
+
"shakapacker" => shakapacker_dependency_value || pj["dependencies"]["shakapacker"].delete_prefix("^")
|
|
206
210
|
})
|
|
207
211
|
}
|
|
208
212
|
else
|