We're just getting up to speed on Capistrano and I could use some help. We're running codeIgniter and would like to allow our devs to run/test locally, push to the Git repo when we've got a complete module. The issue is that in order to run locally, you need to set up the environment to look for the system as well as the models, views and controllers.
Rather than write a bunch of conditional code into index.php that could get munged accidentally or some such, I'd rather have a static file called "index-server.php" and use Capistrano to rename index-server.php to index.php after a deployment is complete.
I'm not even sure what this would look like but I'm pretty sure it's possible.
I'm fairly new to capistrano as well, but I did manage to execute system commands on the deploy target by adding this to deploy.rb
namespace :deploy do
after :finishing, 'deploy:cleanup'
after :publishing, :restart
after :restart, :clear_cache do
on roles(:app), in: :groups, limit: 3, wait: 10 do
execute "/bin/mv #{File.join(release_path, 'index-server.php')} #{File.join(release_path, 'index.php')}"
end
end
end
Or something along those lines. Hope it helps!
Edit: this is with Capistrano 3
Related
I'm looking through the documentation, but I'm not seeing any option to change the working directory used when running tests.
I'm using PhpUnit as it's included in Laravel. I want to be able to run vendor/bin/phpunit from my project's root directory, and have it run using the /public directory as the working directory.
I tried running ../vendor/bin/phpunit from the /public, but since the phpunit.xml file isn't in the public directory and I don't want to specify my config file path every time, that won't work.
Is there something I can add to my phpunit.xml file to tell it to run tests using the /public directory as the "cwd" (current working directory)?
Based on the feedback I received in the comments and the documentation, I determined the following:
It's probably not possible to change the cwd that phpunit uses by default (well, it's possible in PhpStorm, but not the command line without writing some kind of wrapper script)
Code that depends on being run from a specific directory is not a good idea.
What I had was some code in one of my classes like this:
$var = file_get_contents("../some_file.json");
This works fine -- until you try to add unit tests. The web server runs using the /public directory as the cwd, while phpunit will run using the root directory.
Rather than trying to force phpunit to always use a particular cwd (/public), I decided it's probably best to remove relative paths from the code that rely on a consistent cwd. So the line above becomes:
$var = file_get_contents(base_path("some_file.json"));
I didn't want to change production code that was already working just to get some tests in place, but this change seemed insignificant enough. (and it's an improvement anyway)
Well, you'd have to do the actual chdir in PHP, but you can define a bootstrap script in the XML (<phpunit bootstrap="./bootstrap.php">) and have that change the working directory.
Alternatively, you can put a setUpBeforeClass function into your test class that changes the working directory.
I have an existing PHP project with jquery and bootstrap, not using any front-end framework.
I am trying to use webpack module bundler in order to create a single entry point for my project resources, manage js dependencies with node js package manager, run tasks as minify js css, image re-size...etc. And improve the browser loading time required to load a single page.
I came across the webpack tutorials and got to install it and install its dev-server, but the problem is that I am not able to understand how I will convert all my current js scripts and css links in the project (where I have a lot of jquery and CSS libraries used to provide multiple features in the project) to use webpack.
Do I have to rewrite all my JS and CSS files in a way that suits webpack? How do I make a successful migration?
Besides, I am not able to run my current php application on the webpack dev-server, is it meant to run there in the first place? It is only listing the directories of the project in the meantime.
I have created a test index.js file and used the following webpack configuration:
var path = require('path');
var webpack = require('webpack');
module.exports =
{
entry: [
'./public/js/index.js',
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8080'
],
plugins: [
new webpack.HotModuleReplacementPlugin()
],
output: {
path: path.join(__dirname, "public/dist/js"),
publicPath : "http://localhost:8080/my_proj/public/dist/js",
filename: "bundle.js"
}
};
I added the bundle.js to my script loads just for testing as follows hoping that the application will run on the webpack dev-server:
<script type="text/javascript" src="public/dist/js/bundle.js"></script>
<script type="text/javascript" src="public/js/jquery.min.js"></script>
<script type="text/javascript" src="public/js/jquery.migrate.js"></script>
<script type="text/javascript" src="public/js/jquery.bxslider.min.js"></script>
<script type="text/javascript" src="public/js/jquery.appear.js"></script>
<script type="text/javascript" src="public/js/jquery.countTo.js"></script>
<script type="text/javascript" src="public/js/bootstrap.js"></script>
Please help me understand the concept here and how can I make this migration successfully?
First, to answer your smaller questions:
No, you're not supposed to run your PHP application through a webpack dev server. Explained in the Live Reloading section below.
No, you won't have to rewrite your assets. Probably. See the CSS and Edge Cases sections below.
Disclaimer: I'll only take on a small fraction of your question. Its scope is just way too broad to be packed into just one StackOverflow answer.
I will only get in touch with
setting up a development and a production environment for webpack
bundling your first JavaScript
which should give you a foundation to build on.
I'll also mention some things you may want to add and link according resources to read through.
So, let's go.
Requirements
I assume you have Node.js and npm installed on your machine and roughly know how to use them.
I also assume you have webpack and webpack-cli installed as (dev) dependencies of your project (not just globally):
npm install --save-dev webpack webpack-cli
Update: Earlier versions of this answer did not require installing webpack-cli. As of version 4 (February 2018), webpack's CLI resides in its own package, hence the additional required package.
Set up development and a production workflow
You usually want to do stuff differently in development than in production (minifying in production, live-reloading in development, ...)
To achieve that, we'll want to split up our configuration files.
Prepare the directory structure
Let's agree to ignore the webpack config from your question. We'll start all over, we'd have to change almost everything anyway.
First, create a build folder inside your project root. Build-related stuff will go there, since we don't want to pollute your project's root folder with config files. (You're free to name this folder differently, but keep track of that during this tutorial.)
Create a config.base.js, a config.production.js and a config.development.js file inside that folder.
Great, we have config files for two build chains now. The configurations are still empty though, so let's now fill them with some basic logic.
Install webpack-merge
But first, we'll need to install webpack-merge.
npm install --save-dev webpack-merge
This package allows us to deeply merge multiple webpack configurations. We want to use it to create webpack configurations depending on our current environment.
Adjust your configuration
Now adjust your build/config.base.js:
module.exports = {
// We'll place webpack configuration for all environments here
}
The file does obviously just export an empty object right now, but we'll need that for the following steps.
Put this code in your build/config.production.js:
const { merge } = require('webpack-merge')
module.exports = merge(require('./config.base.js'), {
mode: 'production'
// We'll place webpack configuration for production environment here
})
And almost the same code goes into your build/config.development.js:
const { merge } = require('webpack-merge')
module.exports = merge(require('./config.base.js'), {
mode: 'development',
watch: true
// All webpack configuration for development environment will go here
})
I guess it's pretty intuitive what this does:
Using webpack with the config.development.js configuration will fetch the common configuration and merge its own config declaration in.
Update: The mode option in the above config files was added in webpack 4 (released February 2018). It sets a bunch of sensible defaults for development and production bundles.
Now running the process would look like this from the command line:
npx webpack --config build/config.development.js
# If the above doesn't work, you probably have an older version of npm (< 5.1) installed
# While npx is a really great tool, you can of course still call the path of the webpack executable manually:
node_modules/.bin/webpack --config build/config.development.js
...and vice versa for the production environment.
That command is rather clunky to use, but no worries, we'll address that later.
Make some helper files
There are information we'll be wanting to centralize to make them easily exchangeable. File paths are such a thing. So let's extract them.
Create a paths.js in your build folder and have it export some paths we'll want to use later:
const path = require('path')
// I'm really just guessing your project's folder structure from reading your question,
// you might want to adjust this whole section
module.exports = {
// The base path of your source files, especially of your index.js
SRC: path.resolve(__dirname, '..', 'public'),
// The path to put the generated bundle(s)
DIST: path.resolve(__dirname, '..', 'public', 'dist'),
/*
This is your public path.
If you're running your app at http://example.com and I got your DIST folder right,
it'll simply be "/dist".
But if you're running it locally at http://localhost/my/app, it will be "/my/app/dist".
That means you should probably *not* hardcode that path here but write it to a
machine-related config file. (If you don't already have something like that,
google for "dotenv" or something similar.)
*/
ASSETS: '/dist'
}
Create aliases
As mentioned above, we could technically run our build chain in development mode like this:
npx webpack --config build/config.development.js
That's an uncomfortably verbose command though, so let's change that.
It's way more convenient to run your build process via npm scripts. Add one script per environment to your package.json like this:
{
"scripts": {
"dev": "webpack --config build/config.development.js",
"prod": "webpack --config build/config.production.js"
}
}
Now you can run your build chains with npm run dev resp. npm run prod – which is much easier to memorize and faster to type.
...as soon as there'es anything to build, of course.
Bundle JavaScript
Okay, that was actually a fair amount of work without achieving too much so far.
So let's start with something more exciting: We'll define a JavaScript entry point.
Define an entry point
Put the following code into your build/config.base.js (replacing the existing code entirely):
const path = require('path')
const { SRC, DIST, ASSETS } = require('./paths')
module.exports = {
entry: {
scripts: path.resolve(SRC, 'js', 'index.js')
},
output: {
// Put all the bundled stuff in your dist folder
path: DIST,
// Our single entry point from above will be named "scripts.js"
filename: '[name].js',
// The output path as seen from the domain we're visiting in the browser
publicPath: ASSETS
}
}
Create the JavaScript file
The above configuration expects an index.js to live in your SRC/js folder (as defined in the build/paths.js).
Let's create that file with the following content:
import './jquery.min.js'
import './jquery.migrate.js'
import './jquery.bxslider.min.js'
import './jquery.appear.js'
import './jquery.countTo.js'
import './bootstrap.js'
As you can see, the index.js just imports all of the files you want to use.
If you now run
npm run prod
from your terminal, a scripts.js file will be created in your DIST folder. You can include that into your markup with a plain ol' <script> tag.
Congratulations, you've got a working webpack setup!
Dive deeper
This mini-tutorial really just scraped the surface of what you can do with webpack. It gives you a pretty solid foundation for your configuration which you now can fill with whatever you need. And that will actually be quite a lot of stuff.
I'll list a few more things you may want to enhance, with some links to read through.
webpack Concepts
If you want to use webpack, it can be hard to do so if you don't know about its underlying concepts. Juho Vepsäläinen created a great guide on getting started with webpack which helped me a whole lot. He's also a webpack core contributor so you can be sure he knows what he's talking about.
Especially loaders are a concept you'll really need to know.
Many of the hints on this list are also explained there.
Read more: SurviveJS – webpack tutorial
Code Splitting
It does what the name's saying: You might not want to pack all your JavaScript into one bulky output file.
It's a job webpack does for you to split off parts of your bundle which you only need on certain pages of your application.
Also, depending on how frequently you're working on your project's JavaScript, it might be a good idea to split off third party code from your bundle for caching purposes.
Read more: webpack Documentation – Code Splitting
Caching
You may want to enhance your site's caching behaviour by adding a hash to your bundled file names which depends on their content. This will create (for example) a script.31aa1d3cad014475a618.js instead of a scripts.js.
That file can then be cached indefinitely because as soon as its contents change, the file name will change as well.
Your PHP code might then use the webpack-manifest-plugin to gain access to the generated file names.
Read more:
Immutable Caching on how to cache your bundles forever
webpack Documentation – chunkhash on how to enrich your bundle file names with hashes
webpack-manifest-plugin on how to generate a manifest.json containing the file names of your current bundle
Transpiling
In case you want to use modern ES2015 code in your site's JavaScript (and are targeting non-evergreen browsers), you'll want to transpile them down to regular ES5. (If the term "ES2015" does not make any sense to you, you're most likely not using it and can ignore this paragraph.)
Read more: babel-loader – A loader that runs Babel on your scripts
CSS
There are webpack loaders for CSS. And Sass. And PostCSS. Whatever you need.
And since you're probably not planning to include your CSS via <script> tags, get to know the Extract Text Plugin to generate actual .css files.
Update: The Extract Text Plugin is very established. However, it's actually a kind of hack: It generates .css files even though webpack only knows JavaScript as its target language.
However, this is no longer true as of webpack 4. There's now a system to define arbitrary module types, including CSS.
Long story short: Expect native CSS support in webpack to replace Extract Text Plugin some time soon.
Hint: Paths
I'll mention this because this was a real pain point for me until I realized how webpack works here:
Be aware that webpack will recognize your url(...) statements and try to resolve them relative to your source files.
This means, your source file public/css/main.css:
body {
background: url('../img/bg.jpg');
}
if your output path is public/dist/css/bundle.css, will be transformed to:
body {
background: url('../../img/bg.jpg');
}
Read more:
style-loader and css-loader – you'll hands down need those.
extract-text-webpack-plugin
Minifying
Update: Since webpack 4 was released in February 2018, this section is rather obsolete. Setting the new mode option to "production" now automatically applies JavaScript minification.
There is a Terser plugin for webpack to minify your JavaScript. Minifying CSS is a feature already builtin to the css-loader plugin mentioned above.
Read more: Terser webpack Plugin
Image Optimization
webpack is a bundler, not a task runner. Thus, image optimization is not a genuine webpack task. You'd probably be better off using an actual task runner or just defining some npm scripts for this.
This does not mean webpack is not capable of doing this. There are plugins for pretty much everything.
Read more:
imagemin can minify images pretty nicely on its own.
imagemin-webpack-plugin will integrate that process into your webpack build chain.
Live Reloading
Your problems with live reloading have a pretty simple cause: a webpack dev server is just a simple Node.js server serving only static files.
For you're case, webpack-dev-server probably is the wrong tool at all. Try webpack-livereload-plugin instead for a live reloader that you can just include via <script> tag.
Read more: webpack-livereload-plugin
Source Maps
Update: As of webpack 4 (released in February 2018), source maps are automatically generated when the new mode option is set to "development".
By all means, use source maps. They'll make your work with bundles so much easier you'll want to cry.
Read more: webpack Documentation – Source Maps
Edge Cases
Usually, all your existing scripts you'll process with webpack should be running just fine.
The only exception that comes to my mind right now would be regarding global entities.
Look at the following code:
function myFunc () {
console.log('I exist!')
}
This code in a plain JavaScript file would make myFunc available everywhere in your JS code. But since webpack bundle code is wrapped inside callback functions (and thus leaving the global scope), there won't be any access to that function anymore.
Third party libraries should not be a problem there, they usually assign their globals to the window object directly, but if you've already written JS code in your project, you should be aware of that.
Automate!
You'll want to automate as much of your workflow as possible.
Consider running npm run prod via a git hook before pushing / after pulling.
Based on the existing vue-templates and and the answer from #Loilo I made a vue template you can install with vue-cli. This template jumpstarts you for a vue application you can extend with or integrate in you existing environment.
npm install -g vue-cli
vue init delcon/webpack-simple my-project
cd my-project
npm install
devwatch:
This template has an additional run devwatch option that watches for filechanges instead of using the webpack-dev-server. This makes it usable for any existing webserver enviroment.
npm run devwatch
dev:
to run it with the default webpack-dev-server, remove <script src="http://localhost:35729/livereload.js"></script> in index.html:
npm run dev
build:
to build your project for production:
npm run build
I am working on a Symfony 2 WebApp. The WebApp has been online for about two years, now I would like to update the design. This work should be outsourced to a 3rd party designer.
The designer should be able to work an a live version of the WebApp (= actually running on my server instead of just plain files) so that design changes become visible instantly when refreshing the browser. Thus the designer needs to be able to change/add files directly on the server and to refresh the cache, assets and styles (using SASS + Compass) when ever needed.
At the same time, the designer should not have general access to neither the WebApp code nor the server itself.
Giving access to the design files only is already solved: I have moved all necessary files/folders from the Symfony installation to a separate folder that is accessible by FTP by the designer. Within the Symfony installation the files/folders have been replaced by symlinks (see my question here).
This works great. Only problem is, currently cache/asset/style refresh can currently only be triggered by direct access via SSH:
$ php app/console cache:clear --env=prod --no-debug
$ php app/console assetic:dump --env=prod --no-debug
$ compass compile --output-style compressed --force
Is it somehow possible to expose these commands via HTTP(S)? Of course the designer will be working on a dedicated Symfony installation. Thus changes will not have any effect on the live version.
Problem is, that app/console... is outside the domain root of the WebApp. Of course I could set another domain to point to app/console... But this way all other files and folders below this dir would be accessible as well.
Additionally I am not sure, if compass compile... can be run from HTTP at all. How can this be done?
I am aware, that refreshing cache and assets is not absolutely necessary when using the dev front controller (.../app_dev.php/...) but without able to refresh / re-compile the Compass and SASS files, style changes will not become visible.
So: Can this be done by HTTP? Maybe using some proxy-script that is called by PHP?
To expose these commands via HTTP(S), you can add for your designer a route calling an action in a controller to run the PHP commands, as explained in the doc:
// src/AppBundle/Controller/CommandController.php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\HttpFoundation\Response;
class CommandController extends Controller
{
public function refreshAction()
{
$kernel = $this->get('kernel');
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput(
array(
'command' => 'cache:clear',
'--env' => 'prod',
),
array(
'command' => 'assetic:dump',
'--env' => 'prod',
),
);
// You don't need the output
$output = new NullOutput();
$application->run($input, $output);
$content = "Refreshed";
return new Response($content);
}
}
For the compass command, you can use assetic:watch as mentioned in giorgio comment.
I think that it would be best to hide as many things as possible to make it simpler for 3rd party to work with your project. Therefore I'll suggest to handle that automatically behind the scenes.
Therefore:
SASS - I would run Gulp/Grunt watch task server-side that would recompile SASS files on each save. That should work flowlessly. (I'm LESS user, but I guess that works pretty much the same in SASS)
cache:clear - I would recommend you to simply turn off the cache on the instace that the 3rd party will work on. It can be done in a few ways. You can set app_dev.php as entry point instead of app.php, but then you should turn off profiler. You can create separate environment if you don't want to change dev one. Also you probably can change prod configuration.
assetic:dump - I personally don't like Assetic and don't use it, so I may be wrong in this case. Anyway, as I read here, in dev environment there's no need to dump assets as they are delivered dynamically, so the solution for cache issue should work here too.
I use SVN for that, the repo is on our server, the website is a checkout folder and the designer has also a checkout folder.
Make a cron that will make a svn update . and a cache:clear
With that you can grant him access only to the views and maybe controller.
You hide parameters and services and entity from him.
And this is for sure NOT IN PRODUCTION but in integration server
I use CloudControl for hosting and I would like to set up a server (possibly with load balancing support) to host piwik for all of my websites. The only problem is that the only writable directory CloudControlled allows you to access is defined by $_SERVER['TMPDIR'].
Is it possible to modify piwik to use this directory for all of its file-writing needs?
And also will I run into any issues with using load balancing? Something like automatically generated reports being generated by each node behind my load balancer since they're not aware of each other?
The idea is to keep this change for your system even when you update.
This is easy to do: create a bootstrap.php inside the piwik main folder.
This is the content of said file:
<?php
define('PIWIK_USER_PATH', $_SERVER['TMPDIR']);
You can double-check this: in index.php, you should see that it checks for a bootstrap.php file in the same folder. It's included when available, and this allows you to do little customizations and keep them even when you update. E.g. I've run piwik from svn for the past three years or so and have some custom changes in there.
There's far too much code for me to be able to confirm this works, but the constant PIWIK_USER_PATH seems to be used as the base root for file io. With that in mind, editing index.php, around line 23, which is originally:
if(!defined('PIWIK_USER_PATH'))
{
define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT);
}
To something like:
if(!defined('PIWIK_USER_PATH'))
{
define('PIWIK_USER_PATH', $_SERVER['TMPDIR']);
}
Might work - but then what happens when it's trying to read a file in its original location? Since this is a temporary directory, however, it may not be viable, in which case an approach using override_function or a similar method, paired with a persistent storage (your database), might also work - by overriding file functions with a database load/save routine; obviously this opens up another can of worms of biblical proportions, thus, my final recommendation is for you to get another less restrictive host.
I am trying to get into PHP app deployment with Capistrano. I have two config files that I need to be "edited" depending on where I deploy it. It's basic stuff like database name and root url (Codeigniter). Can I make Capistrano edit specified automatically? Let's say I want to edit the following in the file /system/config/edit.php:
$test = '';
// edit to
$test = 'Hello World';
Thanks,
Max
What I generally do in this kind of situation (even though I don't use Capistrano) is to have several config files commited to source control.
For instance :
config.php for development machines
this file is the one that's always used by the application
config.testing.php
config.staging.php
config.production.php
And when deploying the application to the server, I just have to copy the file corresponding to the current environment to "config.php" -- as this one is the one that's always used by the application.
It means that I have to do a file copy during the build process, yes, but :
it means there is no need for any search and replace, that can break
it also means every config files are commited to SVN (or whatever source control software you are using)
If your configuration files become too complex, and duplicate lots of stuff, you can think about having one "default" config file, that's always included, and sub-config files that only define what depends on the environment.
What that, what I said before still stands : just include the "default" file at the begining of each other.
My Unix is knowledge isn't quite up to scratch so I can't quite get the syntax perfect for what you want. However, Capistrano allows you to directly use the Unix command-line by invoking :run_method within your configs.
The Capistrano code might look something like the following:
run "grep -R --files-with-matches '$test = "";' /system/config/ | xargs perl -pi~ -e 's/\$test = "";/$test = "Hello World";/'"
I would check up on that find and replace function working as expected before implementing it live though.
If you need any more help, I'd recommend checking out the Capistrano Handbook, it should answer most of your questions.