├── .gitignore ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── capistrano-file-permissions.gemspec └── lib ├── capistrano-file-permissions.rb └── capistrano ├── file-permissions.rb └── tasks └── file-permissions.rake /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in capistrano-bundler.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Peter Mitchell (peterjmit@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Capistrano::FilePermissions 2 | 3 | File permissions handling for Capistrano v3.* 4 | 5 | ## Installation 6 | 7 | Add this line to your application's Gemfile: 8 | 9 | ```ruby 10 | gem 'capistrano', '~> 3.0.0' 11 | gem 'capistrano-file-permissions' 12 | ``` 13 | 14 | And then execute: 15 | 16 | $ bundle 17 | 18 | Or install it yourself as: 19 | 20 | $ gem install capistrano-file-permissions 21 | 22 | ## Usage 23 | 24 | Require the module in your `Capfile`: 25 | 26 | ```ruby 27 | require 'capistrano/file-permissions' 28 | ``` 29 | 30 | Set the (relative) paths to the files you want to be handled during deployment, 31 | and optionally add a user to give access. 32 | 33 | ```ruby 34 | set :file_permissions_paths, ["app/logs", "app/cache"] 35 | set :file_permissions_users, ["www-data"] 36 | ``` 37 | 38 | ### Acl 39 | 40 | Add the acl task to the deployment flow 41 | 42 | ```ruby 43 | before "deploy:updated", "deploy:set_permissions:acl" 44 | ``` 45 | 46 | Assume `app/logs` is a shared directory, and `app/cache` is part of the normal 47 | release, this gem would execute the following: 48 | 49 | ``` 50 | [..] setfacl -Rn -m u:www-data:rwX -m u::rwX /shared/app/logs //app/cache 51 | ``` 52 | 53 | ### Other tasks 54 | * deploy:set_permissions:chmod 55 | * deploy:set_permissions:chgrp 56 | * deploy:set_permissions:chown 57 | * 58 | ### Configuration 59 | 60 | The gem makes the following configuration variables available (shown with defaults) 61 | 62 | ```ruby 63 | set :file_permissions_roles, :all 64 | set :file_permissions_paths, [] 65 | set :file_permissions_users, [] 66 | set :file_permissions_groups, [] 67 | set :file_permissions_chmod_mode, "0777" 68 | ``` 69 | 70 | ## Contributing 71 | 72 | 1. Fork it 73 | 2. Create your feature branch (`git checkout -b my-new-feature`) 74 | 3. Commit your changes (`git commit -am 'Add some feature'`) 75 | 4. Push to the branch (`git push origin my-new-feature`) 76 | 5. Create new Pull Request 77 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | -------------------------------------------------------------------------------- /capistrano-file-permissions.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = 'capistrano-file-permissions' 7 | spec.version = '1.0.0' 8 | spec.authors = ['Peter Mitchell'] 9 | spec.email = ['peterjmit@gmail.com'] 10 | spec.description = %q{File permissions management for Capistrano 3.x} 11 | spec.summary = %q{File permissions management for Capistrano 3.x} 12 | spec.homepage = 'https://github.com/capistrano/file-permissions' 13 | spec.license = 'MIT' 14 | 15 | spec.files = `git ls-files`.split($/) 16 | spec.require_paths = ['lib'] 17 | 18 | spec.add_dependency 'capistrano', '~> 3.0' 19 | end 20 | -------------------------------------------------------------------------------- /lib/capistrano-file-permissions.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capistrano/file-permissions/0122fe4ab1e36e0608cca7f41a2401b293d8f456/lib/capistrano-file-permissions.rb -------------------------------------------------------------------------------- /lib/capistrano/file-permissions.rb: -------------------------------------------------------------------------------- 1 | load File.expand_path('../tasks/file-permissions.rake', __FILE__) 2 | -------------------------------------------------------------------------------- /lib/capistrano/tasks/file-permissions.rake: -------------------------------------------------------------------------------- 1 | module Capistrano 2 | class FileNotFound < StandardError 3 | end 4 | end 5 | 6 | def absolute_writable_paths 7 | linked_dirs = fetch(:linked_dirs) 8 | fetch(:file_permissions_paths).map do |d| 9 | Array(linked_dirs).include?(d) ? shared_path.join(d) : release_path.join(d) 10 | end 11 | end 12 | 13 | def acl_entries(items, type = 'u', permissions = 'rwX') 14 | items.map { |item| "#{type}:#{item}:#{permissions}" } 15 | end 16 | 17 | namespace :deploy do 18 | namespace :set_permissions do 19 | task :check do 20 | on roles fetch(:file_permissions_roles) do 21 | absolute_writable_paths.each do |path| 22 | unless test "[ -d #{path} ]" or test "[ -e #{path} ]" 23 | msg = "Cannot change permissions: #{path} is not a file or directory" 24 | error msg 25 | fail Capistrano::FileNotFound, msg 26 | end 27 | end 28 | end 29 | end 30 | 31 | desc "Set user/group permissions on configured paths with setfacl" 32 | task :acl => [:check] do 33 | next unless any? :file_permissions_paths 34 | on roles fetch(:file_permissions_roles) do |host| 35 | users = fetch(:file_permissions_users).push(host.user) 36 | entries = acl_entries(users); 37 | paths = absolute_writable_paths 38 | 39 | if any? :file_permissions_groups 40 | entries.push(*acl_entries(fetch(:file_permissions_groups), 'g')) 41 | end 42 | 43 | entries = entries.map { |e| "-m #{e}" }.join(' ') 44 | 45 | execute :setfacl, "-R", entries, *paths 46 | execute :setfacl, "-dR", entries, *paths.map 47 | end 48 | end 49 | 50 | desc "Recursively set mode (from \"file_permissions_chmod_mode\") on configured paths with chmod" 51 | task :chmod => [:check] do 52 | next unless any? :file_permissions_paths 53 | on roles fetch(:file_permissions_roles) do |host| 54 | execute :chmod, "-R", fetch(:file_permissions_chmod_mode), *absolute_writable_paths 55 | end 56 | end 57 | 58 | desc "Recursively change user ownership for configured paths, and make them user writable" 59 | task :chown => [:check] do 60 | next unless any? :file_permissions_paths 61 | next unless any? :file_permissions_users 62 | 63 | users = fetch(:file_permissions_users) 64 | if users.length > 1 65 | warn "More than one user configured, using the first user only" 66 | end 67 | 68 | on roles fetch(:file_permissions_roles) do |host| 69 | paths = absolute_writable_paths 70 | execute :sudo, :chown, "-R", users.first, *paths 71 | end 72 | end 73 | 74 | desc "Recursively change group ownership for configured paths, and make them group writable" 75 | task :chgrp => [:check] do 76 | next unless any? :file_permissions_paths 77 | next unless any? :file_permissions_groups 78 | 79 | groups = fetch(:file_permissions_groups) 80 | if groups.length > 1 81 | warn "More than one group configured, using the first group only" 82 | end 83 | 84 | on roles fetch(:file_permissions_roles) do |host| 85 | paths = absolute_writable_paths 86 | execute :sudo, :chgrp, "-R", groups.first, *paths 87 | # make sure all child directories inherit group writable 88 | execute :sudo, :chmod, "-R", "g+rws", *paths 89 | end 90 | end 91 | end 92 | end 93 | 94 | namespace :load do 95 | task :defaults do 96 | set :file_permissions_roles, :all 97 | set :file_permissions_paths, [] 98 | set :file_permissions_users, [] 99 | set :file_permissions_groups, [] 100 | set :file_permissions_chmod_mode, "0777" 101 | end 102 | end 103 | --------------------------------------------------------------------------------