Deploying Create-React-App with Rails API on Heroku - ruby-on-rails

I'm having an issue getting my react /rails app to work on heroku. I've managed to get it deployed and the rails server starts but I'm not seeing my react app. I feel like I'm close but can't figure out what's missing.
So my process is currently to run npm run build locally from the client directory which builds a 'build' directory in client. I then commit the results of the build and push to Heroku with git push heroku master.
I then navigate to the heroku app in a browser where I'm only getting a blank white page which is an index file the I manually copied from the build dir to public. I'm not sure if the index file is correct but I just wanted to make sure i could hit something.
Ultimately, I would like to just push the repo and it automatically run the build. I've seen this mentioned various places but I always get a react-script does not exist error when I run npm run build on the server.
My configuration is as follows:
basic structure of app
/app - root of Rails app
/client - root of React app
/public - does display index page
root/client/package.json
{
"name": "qc_react",
"version": "0.1.0",
"private": true,
"devDependencies": {
"react-scripts": "^0.8.4"
},
"dependencies": {
"react": "^15.4.1",
"react-dom": "^15.4.1",
"react-router": "^3.0.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"cacheDirectories": [
"node_modules",
"client/node_modules"
],
"proxy": "${API_URL}:${PORT}/v1/"
}
root/package.json
{
"name": "web",
"version": "1.0.0",
"description": "This repo contains a web application codebase. Read instructions on how to install.",
"main": "index.js",
"scripts": {
"build": "cd client" # not sure what to put here but this gets me past build failure
},
"repository": {
"type": "git",
"url": "git+ssh://myrepo.git"
},
"author": "",
"license": "ISC",
"homepage": "https://myhomepage#readme"
}
Procfile
api: bundle exec puma -C config/puma.rb
Buildpacks
1. https://github.com/mars/create-react-app-buildpack.git
2. heroku/ruby
config/puma.rb
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count
preload_app!
rackup DefaultRackup
port ENV['PORT'] || 3001
environment ENV['RACK_ENV'] || 'development'
on_worker_boot do
# Worker specific setup for Rails 4.1+
# See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot
ActiveRecord::Base.establish_connection
end

That's because Rails is serving static files (including index.html) from /public. But your React app bundle is located inside /client/build. After build you need to copy these files back to Rails folder. The easiest way to do this is to add some scripts to your package.json:
"scripts": {
...
"deploy": "cp -a build/. public",
"heroku-postbuild": "npm run build && npm run deploy"
},
heroku-postbuild is executed automatically by Heroku after all dependencies are installed, just like normal postinstall hook.
Be careful with the paths for cp command. Your setup with 2 different package.json files is too complex. I recommend to use a single package.json inside root and set all paths accordingly.

So I finally found the solution I was looking for. My code base has the following structure.
app/
client/
public/
...
I'm currently building my client solution as #yeasayer suggested above. After building and deploying my react project to the public folder in my rails api, I added the following to my routes:
... # api routes ...
get '/*path', to: 'react#index'
Then I created a react_controller and added the following contents:
class ReactController < ActionController::Base
def index
render :file => 'public/index.html', :layout => false
end
end
Now any routes not caught by the api routes, will render the react app. I'm not sure why others don't use this structure instead of using react_on_rails or some other plugin to achieve the same result. This setup is a lot simpler than dealing with these other solutions but I'd like to hear any thoughts on why this solution is not a good idea.

I see you are using the create-react-app-buildpack, but I think your issue is that your react app is in a subdirectory. Buildpacks only get executed if heroku can detect a matching application in the directory and since your package.json in your root does not match create-react-app-buildpack I don't think it is being used.
What you might try is removing the root package.json and using this sub directory buildpack so you can specify the locations of each buildpack directory

Related

Electron-builder doesnt generate dist files

I'm trying to build an installer with electron-builder but every time I generate the installer and install my application, I get an error that "dist/index" doesn't exist in .asar file. I checked and no dist file is packed inside .asar.
The error I'm getting:
Not allowed to load local resource: file:///C:/Users/user1/AppData/Local/Programs/myApp/resources/app.asar/dist/index.html
I'm building with this script:
"publish": "set GH_TOKEN=<my_token> && electron-builder --win -p always"
Does electron-builder have any flags to tell him where to put the output files?
Okey after some trial and error, I found what was wrong...
So basically my package.json was configured wrong. In order to include dist in build it must be specified like this:
...
"build": {
"appId": "si.app.testing",
...
"directories": {
"output": "release",
"buildResources": "dist"
},
"files": [
"**/*",
"dist/**/*",
...
"!.github",
"!.vs",
"!node_modules/*"
]
},
...

Next.js: how to use different env file for different environment?

Below is the code from package.json file
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
},
Below is my next.config.js file, here console.log always undefined
require("dotenv").config();
console.log(process.env.BASE_URL)
module.exports = {
env: {
API_BASE_URL: process.env.BASE_URL
},
reactStrictMode: true,
}
And this is in the .env.development
BASE_URL: 'http://localhost:3000'
When I ran the npm run dev command,
it prints on terminal "Loaded env from /var/www/html/next/next-SC/.env.development"
So, why the console prints undefined always.
I'm using next js version "10.0.4"
I assume you are using React with nextjs. If not, then please disregard this answer. I am doing the same thing. React has built in support for env vars. All you need to do is to prefix REACT_APP to your environment vars. So, in your .env.development or .env.staging, etc., you can have REACT_APP_BASE_URL=https://blah.com. You can then access them in your app using process.env.REACT_APP_BASE_URL. Then to build based on environment, I have (I am using craco, you would just use your normal build command)
"build:nightly": "env-cmd -f .env.nightly craco build",
"build:nightly": "env-cmd -f .env.staging craco build",
"build:nightly": "env-cmd -f .env.beta craco build",
...
For development environment, name the file .env.development, for production .env.production.
Next.js has built-in loader for environment variables. So dotenv or similar packages aren't needed. Just add the files. It will be loaded automatically (see documentation).

How to get RSpec working with debugger IDE in VSCode

Despite having spent about the last three hours trying to get this working I cannot for the life of my get RSpec to work with the debugger on VSCode. I can get Rspec to run in the terminal on VSCode but that doesn't give me any of the IDE's debugging functionality for inspecting and stepping.
This is what I've got in my launch.json file:
{
"version": "0.2.0",
"configurations": [
{
"name": "Run RSpec - all",
"type": "Ruby",
"request": "launch",
"cwd": "${workspaceRoot}",
"program": "/Users/my-home-dir/.rvm/gems/ruby-2.6.5#project-gemset-name/wrappers/rspec",
"pathToRDebugIDE": "/Users/my-home-dir/.rvm/gems/ruby-2.6.5#project-gemset-name/gems/ruby-debug-ide-0.7.2",
"args": [
"--pattern",
"${workspaceRoot}/spec/**/*_rspec.rb"
]
}
]
}
And my gemfile contains:
gem 'ruby-debug-ide', '~>0.7.2'
gem 'debase', '~>0.2.4.1'
I've got a feeling that the errors may be coming about due to in incompatibility between RVM and VSCode but I've no idea how to unwind that issue.
This was all setup as per the Microsoft recipe here: https://github.com/Microsoft/vscode-recipes/tree/master/debugging-Ruby-on-Rails
Every time I run this setup I get the following error in the debug console:
Debugger terminal error: Process failed: spawn rdebug-ide ENOENT
Is there any way to get this to run? Also is there any way to get it to use guard so that it runs automatically?
I got it working. Unfortunately, I don't use RVM. So, my solution involves rbenv. I'm sharing it here anyway in case it helps you, or someone else.
which rspec pointed me to the shim (shell script) that rbenv uses to execute the version of rspec installed under the current version of Ruby. When I configured launch.json with that path rdebug-ide didn't like the shim. I assume it was expecting the executable.
So, I ran rbenv which rspec and got the actual path to the executable. Once I plugged that into launch.json it worked fine. Of course, if I change the version of Ruby I'm running, I'll have to update the file to point to the version of RSpec installed under the new version of Ruby.
Given the prevalence of Ruby version managers among the community, I would think ruby-debug-ide would have considered this. Perhaps it's worth an issue on their GitHub: https://github.com/ruby-debug/ruby-debug-ide.
I added a preLaunchTask to launch rdebug-ide server then looked for a pattern before attaching the debugger.
I used the focus flag for controlling which tests are run or not. Am able to set breakpoints in main script files as well as *_spec.rb files.
Gemfile:
group :test, :development do
gem "ruby-debug-ide", "~> 0.7.2"
gem "debase", "~> 0.2.4"
end
launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for rdebug-ide rspec",
"type": "Ruby",
"request": "attach",
"remoteHost": "127.0.0.1",
"remotePort": "1234",
"preLaunchTask": "run-rdebug-for-rspec",
"remoteWorkspaceRoot": "${workspaceRoot}",
"cwd": "${workspaceFolder}"
}
]
}
tasks.json
{
"version": "2.0.0",
"tasks": [{
"label": "run-rdebug-for-rspec",
"command": "bundle",
// could update these are you see fit
// my tests were located in /spec folder
"args": [
"exec","rdebug-ide",
"--host","0.0.0.0",
"--port","1234",
"--dispatcher-port","26162",
// needed to specify the full path of bundle
// can be found by `which bundle`
"--", "/usr/local/bin/bundle", "exec", "rspec", "spec/"
],
"type": "shell",
"isBackground": true,
// this will look for a pattern to attach to rdebug server
// attaching to the server will start running the tests
"problemMatcher": [
{
"pattern": [
{
"regexp": ".",
"file": 1,
"location": 2,
"message": 3
}
],
"background": {
"activeOnStart": true,
// once server is up and running it'll display something like:
// Fast Debugger (ruby-debug-ide 0.7.2, debase 0.2.4.1, file filtering is supported) listens on 0.0.0.0:1234
"beginsPattern": "^Fast Debugger.*1234$",
"endsPattern": ".",
}
}
],
// open up a new task window everytime
// if set to default `shared` then previous task window needs to be closed
// otherwise, was having issues getting patternMatcher to work
// and for rdebug to attach within a shared terminal on successive runs, or restarts.
"presentation": {
"panel": "new"
}
}]
}
While the answer above may work, there will be problems as soon as you upgrade your ruby version because the paths are hard coded.
Here is a less brittle approach that should be able to survive a version upgrade without any problem.
I'm on ruby 3.0.3, managed by rvm on mac osx.
Gemfile includes:
group :development do
gem 'ruby-debug-ide'
gem 'debase', "~> 0.2.5.beta2"
end
(the stable version of debase didn't support ruby v3, hence the beta)
Launch config use the GEM_HOME environment variable to specify the rspec binary (vscode default configuration puts this in workspace root for some reason):
{
"name": "RSpec - all",
"type": "Ruby",
"request": "launch",
"program": "${env:GEM_HOME}/bin/rspec",
"args": [
"-I",
"${workspaceRoot}"
]
}

How to create release channels with electron/electron-builder?

I have an Electron app where I want to introduce parallel release channels: stable, next (for early adopters) and dev (for testing the latest build).
These will have a branch each, with new features appearing first in dev, progressing to next for beta testing and finally moving into stable.
I'm using electron-builder to make these release packages, and I want each to have its own auto-updates - so when I publish a new next release all the users with it get the update.
I want the applications to be independent - a user can have two channels installed and run both at the same time. They'll have different names and different icons.
I can manually set these up in the branches, but really I want to automate this as much as possible - a publish from the next branch should use the right name, icons, IDs and updater without risk of it going to the wrong channel.
Is there a way to do this with electron or electron-builder?
It's possible with electron-builder. I would have several build configurations and tell electron-builder which to use when building.
For example, create file config/beta.json with the following setup:
{
"appId": "com.company.beta",
"productName": "App Beta",
"directories": {
"buildResources": "build/beta" // directory containing your build-specific assets (e.g., beta icons - icon.icns, icon.ico & background.png)
},
"mac": {
"category": "public.app-category.finance"
},
"win": {
"target": [
"nsis"
]
},
"nsis": {
"perMachine": false
},
"publish": [
{
"provider": "s3",
"bucket": "com-app-beta" // dedicated S3 bucket for each build
}
],
}
And duplicate config/beta.json for next.json and current.json (make sure to edit settings accordingly).
In package.json, add the following build scripts (note --em.name=app-beta to overwrite package.json's "name" value):
{
"scripts": {
"build": "build -owl --x64 --config ./config/current.json -p always --em.name=app",
"build-beta": "build -owl --x64 --config ./config/beta.json -p always --em.name=app-beta",
"build-next": "build -owl --x64 --config ./config/next.json -p always --em.name=app-next"
}
}
Run build script when ready to deploy:
npm run build-beta
Using electron-builder version 20.15.1 and MacOS, #Jon Saw's solution needs a minor change because em option is not valid:
"build-beta": "build -owl --x64 --config ./config/beta.json -p always -c.extraMetadata.name=app-beta"

Where is Electron's app.getAppPath() pointing to?

I am using browserify to merge all the .js files of my app into a dist/main.js. My package.json looks like:
"main": "./dist/main.js",
"scripts": {
"start": "electron ./dist/main.js",
},
"bin": {
"electron": "./node_modules/.bin/electron"
}
and I can correctly run my application with npm run start.
However if in main.js I use app.getAppPath() I get:
/home/myuser/projects/electronProject/node_modules/electron/dist/resources/default_app.asar
I would expect this to be
/home/myuser/projects/electronProject/dist/main.js
Did I misunderstood the usage of this method? How can I get the path of the Electron program entrypoint? What is the role of default_app.asar?
Thanks
Why aren't you using __dirname (node.js) or process.resourcesPath (electron)?
https://github.com/electron/electron/blob/master/docs/api/process.md
https://nodejs.org/docs/latest/api/globals.html#globals_dirname
It returns the current application directory:
app.getAppPath()
Returns String - The current application directory.
From the docs.
An asar file is a simple archive format that just appends the files to each other. I'm not sure exactly how you're building the application but tools like electron-packager and electron-builder output the files into a resources/app.asar archive and run the files from there. That means that your current application directory is going to be something/resources/app.asar. From there your main file is located at something/resources/app.asar/main.js.
For whom may ran into the same problem...
It's maybe a problem with your electron configuration field main in package.json
The script specified by the main field is the startup script of your
app, which will run the main process.
The example code from offical websites:
{
"name": "your-app",
"version": "0.1.0",
"main": "main.js",
"scripts": {
"start": "electron ."
}
}
app.getAppPath() output:
YOUR_PATH_TO/electron-quick-start
If you change the code snippet to
{
"name": "your-app",
"version": "0.1.0",
"main": "main.js",
"scripts": {
"start": "electron YOUR_PATH_TO/main.js"
}
}
Then app.getAppPath() output:
YOUR_PATH_TO/electron-quick-start/node_modules/electron/dist/resources/default_app.asar
So the consolution is : If you want to change the startup script, change it in the main field, not just change it in scritps field...

Resources