diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7db42649..f59e22c2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org).
+## UNRELEASED
+
+### Added
+
+- support for directory modes for repos [\#599](https://github.com/puppetlabs/puppetlabs-vcsrepo/issues/599) ([robbat2](https://github.com/robbat2))
+
## [v5.4.0](https://github.com/puppetlabs/puppetlabs-vcsrepo/tree/v5.4.0) (2023-01-31)
[Full Changelog](https://github.com/puppetlabs/puppetlabs-vcsrepo/compare/v5.3.0...v5.4.0)
diff --git a/README.md b/README.md
index ad38e235..fdc28790 100644
--- a/README.md
+++ b/README.md
@@ -785,37 +785,37 @@ For information on the classes and types, see the [REFERENCE.md](https://github.
Features: `bare_repositories`, `depth`, `multiple_remotes`, `reference_tracking`, `ssh_identity`, `submodules`, `user`
-Parameters: `depth`, `ensure`, `excludes`, `force`, `group`, `identity`, `owner`, `path`, `provider`, `remote`, `revision`, `source`, `user`
+Parameters: `depth`, `ensure`, `excludes`, `force`, `group`, `identity`, `owner`, `path`, `provider`, `remote`, `revision`, `source`, `user`, `mode`
##### `bzr` - Supports the Bazaar VCS.
Features: `reference_tracking`
-Parameters: `ensure`, `excludes`, `force`, `group`, `owner`, `path`, `provider`, `revision`, `source`
+Parameters: `ensure`, `excludes`, `force`, `group`, `owner`, `path`, `provider`, `revision`, `source`, `mode`
##### `cvs` - Supports the CVS VCS.
Features: `cvs_rsh`, `gzip_compression`, `modules`, `reference_tracking`, `user`
-Parameters: `compression`, `cvs_rsh`, `ensure`, `excludes`, `force`, `group`, `module`, `owner`, `path`, `provider`
+Parameters: `compression`, `cvs_rsh`, `ensure`, `excludes`, `force`, `group`, `module`, `owner`, `path`, `provider`, `mode`
##### `hg` - Supports the Mercurial VCS.
Features: `reference_tracking`, `ssh_identity`, `user`
-Parameters: `ensure`, `excludes`, `force`, `group`, `identity`, `owner`, `path`, `provider`, `revision`, `source`, `user`
+Parameters: `ensure`, `excludes`, `force`, `group`, `identity`, `owner`, `path`, `provider`, `revision`, `source`, `user`, `mode`
##### `p4` - Supports the Perforce VCS.
Features: `p4config`, `reference_tracking`
-Parameters: `ensure`, `excludes`, `force`, `group`, `owner`, `p4config`, `path`, `provider`, `revision`, `source`
+Parameters: `ensure`, `excludes`, `force`, `group`, `owner`, `p4config`, `path`, `provider`, `revision`, `source`, `mode`
##### `svn` - Supports the Subversion VCS.
Features: `basic_auth`, `configuration`, `conflict`, `depth`, `filesystem_types`, `reference_tracking`
-Parameters: `basic_auth_password`, `basic_auth_username`, `configuration`, `conflict`, `ensure`, `excludes`, `force`, `fstype`, `group`, `includes`, `owner`, `path`, `provider`, `revision`, `source`, `trust_server_cert`
+Parameters: `basic_auth_password`, `basic_auth_username`, `configuration`, `conflict`, `ensure`, `excludes`, `force`, `fstype`, `group`, `includes`, `owner`, `path`, `provider`, `revision`, `source`, `trust_server_cert`, `mode`
#### Features
diff --git a/REFERENCE.md b/REFERENCE.md
index d3486f91..563d3e10 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -77,6 +77,7 @@ The following parameters are available in the `vcsrepo` type.
* [`trust_server_cert`](#-vcsrepo--trust_server_cert)
* [`umask`](#-vcsrepo--umask)
* [`user`](#-vcsrepo--user)
+* [`mode`](#-vcsrepo--mode)
##### `basic_auth_password`
@@ -203,3 +204,6 @@ Sets the umask to be used for all repo operations
The user to run for repository operations
+##### `mode`
+
+Sets the mode for the repository directory (non-recursive).
diff --git a/lib/puppet/provider/vcsrepo.rb b/lib/puppet/provider/vcsrepo.rb
index ca5963f7..db88213c 100644
--- a/lib/puppet/provider/vcsrepo.rb
+++ b/lib/puppet/provider/vcsrepo.rb
@@ -15,18 +15,26 @@ def check_force
private
- def set_ownership
+ def set_ownership_and_permissions
owner = @resource.value(:owner) || nil
group = @resource.value(:group) || nil
+ mode = @resource.value(:mode) || nil
excludes = @resource.value(:excludes) || nil
+ # Change the permission on the repo itself.
+ # The VCS should maintain permissions on files within the checkout.
+ FileUtils.chmod(mode, @resource.value(:path)) unless mode.nil?
+ # We might have no work to do, and this makes it easier for the callers.
+ return if owner.nil? && group.nil?
+
if excludes.nil? || excludes.empty?
- FileUtils.chown_R(owner, group, @resource.value(:path))
+ FileUtils.chown_R(owner, group, @resource.value(:path)) unless owner.nil? && group.nil?
else
- FileUtils.chown(owner, group, files)
+ FileUtils.chown(owner, group, files) unless owner.nil? && group.nil?
end
end
def files
+ # Expensive on large repos
excludes = @resource.value(:excludes)
path = @resource.value(:path)
Dir["#{path}/**/*"].reject { |f| excludes.any? { |p| f.start_with?("#{path}/#{p}") } }
diff --git a/lib/puppet/provider/vcsrepo/bzr.rb b/lib/puppet/provider/vcsrepo/bzr.rb
index 30ac813e..95b84908 100644
--- a/lib/puppet/provider/vcsrepo/bzr.rb
+++ b/lib/puppet/provider/vcsrepo/bzr.rb
@@ -103,6 +103,6 @@ def clone_repository(revision)
end
def update_owner
- set_ownership if @resource.value(:owner) || @resource.value(:group)
+ set_ownership_and_permissions if @resource.value(:owner) || @resource.value(:group) || @resource.value(:mode)
end
end
diff --git a/lib/puppet/provider/vcsrepo/cvs.rb b/lib/puppet/provider/vcsrepo/cvs.rb
index 651dc78a..2bc8c226 100644
--- a/lib/puppet/provider/vcsrepo/cvs.rb
+++ b/lib/puppet/provider/vcsrepo/cvs.rb
@@ -136,7 +136,7 @@ def create_repository(path)
end
def update_owner
- set_ownership if @resource.value(:owner) || @resource.value(:group)
+ set_ownership_and_permissions if @resource.value(:owner) || @resource.value(:group) || @resource.value(:mode)
end
def runcvs(*args)
diff --git a/lib/puppet/provider/vcsrepo/git.rb b/lib/puppet/provider/vcsrepo/git.rb
index e7400254..79f70808 100644
--- a/lib/puppet/provider/vcsrepo/git.rb
+++ b/lib/puppet/provider/vcsrepo/git.rb
@@ -39,7 +39,7 @@ def create
end
end
- update_owner_and_excludes
+ update_owner_permission_and_excludes
end
def destroy
@@ -101,7 +101,7 @@ def revision=(desired)
if !ensure_bare_or_mirror? && @resource.value(:submodules) == :true
update_submodules
end
- update_owner_and_excludes
+ update_owner_permission_and_excludes
end
def bare_exists?
@@ -239,7 +239,7 @@ def update_references
at_path do
git_remote_action('fetch', @resource.value(:remote))
git_remote_action(*fetch_tags_args, @resource.value(:remote))
- update_owner_and_excludes
+ update_owner_permission_and_excludes
end
end
@@ -256,6 +256,8 @@ def convert_working_copy_to_bare
FileUtils.mv(File.join(@resource.value(:path), '.git'), tempdir)
FileUtils.rm_rf(@resource.value(:path))
FileUtils.mv(tempdir, @resource.value(:path))
+ FileUtils.chown(@resource.value(:user), @resource.value(:group), @resource.value(:path)) if @resource.value(:user) || @resource.value(:group)
+ FileUtils.chmod(@resource.value(:mode), @resource.value(:path)) if @resource.value(:mode)
at_path do
exec_git('config', '--local', '--bool', 'core.bare', 'true')
return unless @resource.value(:ensure) == :mirror
@@ -275,13 +277,15 @@ def convert_bare_to_working_copy
notice 'Converting bare repository to working copy repository'
FileUtils.mv(@resource.value(:path), tempdir)
FileUtils.mkdir(@resource.value(:path))
+ FileUtils.chown(@resource.value(:user), @resource.value(:group), @resource.value(:path)) if @resource.value(:user) || @resource.value(:group)
+ FileUtils.chmod(@resource.value(:mode), @resource.value(:path)) if @resource.value(:mode)
FileUtils.mv(tempdir, File.join(@resource.value(:path), '.git'))
if commits?
at_path do
exec_git('config', '--local', '--bool', 'core.bare', 'false')
reset('HEAD')
git_with_identity('checkout', '--force')
- update_owner_and_excludes
+ update_owner_permission_and_excludes
end
end
set_no_mirror if mirror?
@@ -415,7 +419,8 @@ def init_repository
else
# normal init
FileUtils.mkdir(@resource.value(:path))
- FileUtils.chown(@resource.value(:user), nil, @resource.value(:path)) if @resource.value(:user)
+ FileUtils.chown(@resource.value(:user), @resource.value(:group), @resource.value(:path)) if @resource.value(:user) || @resource.value(:group)
+ FileUtils.chmod(@resource.value(:mode), @resource.value(:path)) if @resource.value(:mode)
args = ['init']
if @resource.value(:ensure) == :bare
args << '--bare'
@@ -598,10 +603,8 @@ def get_revision(rev = 'HEAD')
end
# @!visibility private
- def update_owner_and_excludes
- if @resource.value(:owner) || @resource.value(:group)
- set_ownership
- end
+ def update_owner_permission_and_excludes
+ set_ownership_and_permissions if @resource.value(:owner) || @resource.value(:group) || @resource.value(:mode)
set_excludes if @resource.value(:excludes)
end
diff --git a/lib/puppet/provider/vcsrepo/hg.rb b/lib/puppet/provider/vcsrepo/hg.rb
index e626d988..6f485b7c 100644
--- a/lib/puppet/provider/vcsrepo/hg.rb
+++ b/lib/puppet/provider/vcsrepo/hg.rb
@@ -116,7 +116,7 @@ def clone_repository(revision)
end
def update_owner
- set_ownership if @resource.value(:owner) || @resource.value(:group)
+ set_ownership_and_permissions if @resource.value(:owner) || @resource.value(:group) || @resource.value(:mode)
end
def sensitive?
diff --git a/lib/puppet/provider/vcsrepo/p4.rb b/lib/puppet/provider/vcsrepo/p4.rb
index 8b213a16..60249fb4 100644
--- a/lib/puppet/provider/vcsrepo/p4.rb
+++ b/lib/puppet/provider/vcsrepo/p4.rb
@@ -101,7 +101,7 @@ def source=(_desired)
private
def update_owner
- set_ownership if @resource.value(:owner) || @resource.value(:group)
+ set_ownership_and_permissions if @resource.value(:owner) || @resource.value(:group) || @resource.value(:mode)
end
# Sync the client workspace files to head or specified revision.
diff --git a/lib/puppet/provider/vcsrepo/svn.rb b/lib/puppet/provider/vcsrepo/svn.rb
index c0d985ec..47700f0a 100644
--- a/lib/puppet/provider/vcsrepo/svn.rb
+++ b/lib/puppet/provider/vcsrepo/svn.rb
@@ -265,7 +265,7 @@ def create_repository(path)
end
def update_owner
- set_ownership if @resource.value(:owner) || @resource.value(:group)
+ set_ownership_and_permissions if @resource.value(:owner) || @resource.value(:group) || @resource.value(:mode)
end
def update_includes(paths)
diff --git a/lib/puppet/type/vcsrepo.rb b/lib/puppet/type/vcsrepo.rb
index e06bf6a7..35bf828b 100644
--- a/lib/puppet/type/vcsrepo.rb
+++ b/lib/puppet/type/vcsrepo.rb
@@ -215,6 +215,10 @@ def insync?(is)
desc 'The group/gid that owns the repository files'
end
+ newparam :mode do
+ desc 'The permission for the repository directory itself (not recursive)'
+ end
+
newparam :user do
desc 'The user to run for repository operations'
end
diff --git a/spec/acceptance/clone_repo_spec.rb b/spec/acceptance/clone_repo_spec.rb
index 48e04b6a..5ce4fb96 100644
--- a/spec/acceptance/clone_repo_spec.rb
+++ b/spec/acceptance/clone_repo_spec.rb
@@ -624,4 +624,25 @@
it { is_expected.to be_mode '664' }
end
end
+
+ context 'with mode' do
+ pp = <<-MANIFEST
+ vcsrepo { "#{tmpdir}/testrepo_mode":
+ ensure => present,
+ provider => git,
+ source => "file://#{tmpdir}/testrepo.git",
+ mode => 'u=rwX,g=rX,o=X',
+ }
+ MANIFEST
+ it 'clones a repo' do
+ # Run it twice and test for idempotency
+ idempotent_apply(pp)
+ end
+
+ describe file("#{tmpdir}/testrepo_mode") do
+ # NOTE: '0664' is not supported by 'be_mode'; this must be three digits
+ # unless the first octet is non-zero.
+ it { is_expected.to be_mode '751' }
+ end
+ end
end