diff --git a/REFERENCE.md b/REFERENCE.md
index 6b8d0a0..691f25a 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -16,7 +16,7 @@
### Defined types
-* [`gitlab::custom_hook`](#gitlab--custom_hook): Manage custom hook files within a GitLab project. Custom hooks can be created as a pre-receive, post-receive, or update hook. It's possible to create different custom hook types for the same project - one each for pre-receive, post-receive and update.
+* [`gitlab::custom_hook`](#gitlab--custom_hook): Manage custom hook files within a GitLab project. Custom hooks can be created as a pre-receive, post-receive, or update hook. Only one of each is currently supported by this module.
* [`gitlab::global_hook`](#gitlab--global_hook): Manage global chain loaded hook files for all GitLab projects. Hooks can be created as a pre-receive, post-receive, or update hook. It's possible to create multipe hooks per type as long as their names are unique. Support for chained (global) hooks is introduced in GitLab Shell 4.1.0 and GitLab 8.15.
* [`gitlab::system_hook`](#gitlab--system_hook): A file hook will run on each event so it's up to you to filter events or projects
@@ -1185,7 +1185,7 @@ Default value: `$gitlab::skip_post_deployment_migrations`
### `gitlab::custom_hook`
-Manage custom hook files within a GitLab project. Custom hooks can be created as a pre-receive, post-receive, or update hook. It's possible to create different custom hook types for the same project - one each for pre-receive, post-receive and update.
+Manage custom hook files within a GitLab project. Custom hooks can be created as a pre-receive, post-receive, or update hook. Only one of each is currently supported by this module.
#### Examples
@@ -1193,35 +1193,50 @@ Manage custom hook files within a GitLab project. Custom hooks can be created as
```puppet
gitlab::custom_hook { 'my_custom_hook':
- namespace => 'my_group',
- project => 'my_project',
- type => 'post-receive',
- source => 'puppet:///modules/my_module/post-receive',
+ namespace => 'my_group',
+ project => 'my_project',
+ type => 'post-receive',
+ source => 'puppet:///modules/my_module/post-receive',
+}
+```
+
+##### Calculate hashed storage path
+
+```puppet
+gitlab::custom_hook { 'my_custom_hook':
+ project => 93,
+ hashed_storage => true,
+ type => 'post-receive',
+ source => 'puppet:///modules/my_module/post-receive',
}
+# Hook path will be `@hashed/6e/40/6e4001871c0cf27c7634ef1dc478408f642410fd3a444e2a88e301f5c4a35a4d`
```
#### Parameters
The following parameters are available in the `gitlab::custom_hook` defined type:
-* [`namespace`](#-gitlab--custom_hook--namespace)
* [`project`](#-gitlab--custom_hook--project)
+* [`namespace`](#-gitlab--custom_hook--namespace)
* [`type`](#-gitlab--custom_hook--type)
* [`content`](#-gitlab--custom_hook--content)
* [`source`](#-gitlab--custom_hook--source)
* [`repos_path`](#-gitlab--custom_hook--repos_path)
+* [`hashed_storage`](#-gitlab--custom_hook--hashed_storage)
-##### `namespace`
+##### `project`
-Data type: `String`
+Data type: `Variant[String,Integer]`
-The GitLab group namespace for the project.
+The GitLab project name, or the hashed directory name or project ID number
-##### `project`
+##### `namespace`
-Data type: `String`
+Data type: `Optional[String]`
-The GitLab project name.
+The GitLab group namespace for the project.
+
+Default value: `undef`
##### `type`
@@ -1253,6 +1268,14 @@ The GitLab shell repos path. This defaults to '/var/opt/gitlab/git-data/reposito
Default value: `undef`
+##### `hashed_storage`
+
+Data type: `Boolean`
+
+Whether to treat the project name as a hashed storage directory name or ID number
+
+Default value: `false`
+
### `gitlab::global_hook`
Manage global chain loaded hook files for all GitLab projects. Hooks can be created as a pre-receive, post-receive, or update hook. It's possible to create multipe hooks per type as long as their names are unique. Support for chained (global) hooks is introduced in GitLab Shell 4.1.0 and GitLab 8.15.
diff --git a/manifests/custom_hook.pp b/manifests/custom_hook.pp
index 5df09a0..247c68e 100644
--- a/manifests/custom_hook.pp
+++ b/manifests/custom_hook.pp
@@ -1,26 +1,38 @@
-# @summary Manage custom hook files within a GitLab project. Custom hooks can be created as a pre-receive, post-receive, or update hook. It's possible to create different custom hook types for the same project - one each for pre-receive, post-receive and update.
+# @summary Manage custom hook files within a GitLab project. Custom hooks can be created as a pre-receive, post-receive, or update hook. Only one of each is currently supported by this module.
#
# @example Custom hook usage
# gitlab::custom_hook { 'my_custom_hook':
-# namespace => 'my_group',
-# project => 'my_project',
-# type => 'post-receive',
-# source => 'puppet:///modules/my_module/post-receive',
+# namespace => 'my_group',
+# project => 'my_project',
+# type => 'post-receive',
+# source => 'puppet:///modules/my_module/post-receive',
# }
#
+# @example Calculate hashed storage path
+# gitlab::custom_hook { 'my_custom_hook':
+# project => 93,
+# hashed_storage => true,
+# type => 'post-receive',
+# source => 'puppet:///modules/my_module/post-receive',
+# }
+# # Hook path will be `@hashed/6e/40/6e4001871c0cf27c7634ef1dc478408f642410fd3a444e2a88e301f5c4a35a4d`
+#
+# @param project The GitLab project name, or the hashed directory name or project ID number
# @param namespace The GitLab group namespace for the project.
-# @param project The GitLab project name.
# @param type The custom hook type. Should be one of pre-receive, post-receive, or update.
# @param content Specify the custom hook contents either as a string or using the template function. If this paramter is specified source parameter must not be present.
# @param source Specify a file source path to populate the custom hook contents. If this paramter is specified content parameter must not be present.
# @param repos_path The GitLab shell repos path. This defaults to '/var/opt/gitlab/git-data/repositories' if not present.
+# @param hashed_storage Whether to treat the project name as a hashed storage directory name or ID number
+#
define gitlab::custom_hook (
- String $namespace,
- String $project,
+ Variant[String,Integer] $project,
Enum['update', 'post-receive', 'pre-receive'] $type,
+ Optional[String] $namespace = undef,
Optional[String] $content = undef,
Optional[String] $source = undef,
Optional[Stdlib::Absolutepath] $repos_path = undef,
+ Boolean $hashed_storage = false,
) {
if $repos_path {
$_repos_path = $repos_path
@@ -38,7 +50,29 @@
fail("gitlab::custom_hook[${name}]: Must specify either content or source, but not both")
}
- $hook_path = "${_repos_path}/${namespace}/${project}.git/custom_hooks"
+ if ! ($hashed_storage) and ! ($namespace) {
+ fail("gitlab::custom_hook[${name}]: Must specify either namespace or hashed_storage")
+ }
+
+ if ($hashed_storage) and ($namespace) {
+ fail("gitlab::custom_hook[${name}]: Must specify either namespace or hashed_storage, but not both")
+ }
+
+ if ($namespace) {
+ $hook_path = "${_repos_path}/${namespace}/${project}.git/custom_hooks"
+ } elsif ($hashed_storage) {
+ if ($project.is_a(Integer)) {
+ $_project_hash = sha256(String($project))
+ } else {
+ $_project_hash = $project
+ }
+
+ if ($_project_hash.length != 64) {
+ fail("gitlab::custom_hook[${name}]: Invalid project hash ${_project_hash}")
+ }
+
+ $hook_path = "${_repos_path}/@hashed/${_project_hash[0,2]}/${_project_hash[2,2]}/${_project_hash}.git/custom_hooks"
+ }
File {
owner => $gitlab::service_user,
diff --git a/spec/defines/custom_hook_spec.rb b/spec/defines/custom_hook_spec.rb
new file mode 100644
index 0000000..949d0ab
--- /dev/null
+++ b/spec/defines/custom_hook_spec.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'gitlab::custom_hook' do
+ let(:title) { 'test-hook' }
+
+ let(:pre_condition) do
+ <<-MANIFEST
+ class { 'gitlab':
+ repository_configuration => {},
+ }
+ MANIFEST
+ end
+
+ ['post-receive', 'pre-receive', 'update'].each do |type|
+ context "with type => #{type} and source" do
+ let(:source) { 'puppet:///modules/my_module/post-receive' }
+ let(:params) do
+ {
+ type: type,
+ repos_path: '/custom/hooks/dir',
+ source: source,
+ namespace: 'foo',
+ project: 'bar'
+ }
+ end
+
+ it { is_expected.to compile }
+
+ it do
+ is_expected.to contain_file('/custom/hooks/dir/foo/bar.git/custom_hooks').
+ with_ensure('directory')
+ end
+
+ it do
+ is_expected.to contain_file("/custom/hooks/dir/foo/bar.git/custom_hooks/#{type}").
+ with_ensure('file').
+ with_source(source)
+ end
+ end
+
+ context "with type => #{type} and content" do
+ let(:content) { "#!/usr/bin/env bash\ntest 0" }
+ let(:params) do
+ {
+ type: type,
+ repos_path: '/custom/hooks/dir',
+ content: content,
+ namespace: 'foo',
+ project: 'bar'
+ }
+ end
+
+ it { is_expected.to compile }
+
+ it do
+ is_expected.to contain_file('/custom/hooks/dir/foo/bar.git/custom_hooks').
+ with_ensure('directory')
+ end
+
+ it do
+ is_expected.to contain_file("/custom/hooks/dir/foo/bar.git/custom_hooks/#{type}").
+ with_ensure('file').
+ with_content(content)
+ end
+ end
+
+ context "with type => #{type} and project hash" do
+ let(:content) { "#!/usr/bin/env bash\ntest 0" }
+ let(:params) do
+ {
+ type: type,
+ repos_path: '/custom/hooks/dir',
+ content: content,
+ hashed_storage: true,
+ project: '6e4001871c0cf27c7634ef1dc478408f642410fd3a444e2a88e301f5c4a35a4d'
+ }
+ end
+
+ it { is_expected.to compile }
+
+ it do
+ is_expected.to contain_file('/custom/hooks/dir/@hashed/6e/40/6e4001871c0cf27c7634ef1dc478408f642410fd3a444e2a88e301f5c4a35a4d.git/custom_hooks').
+ with_ensure('directory')
+ end
+
+ it do
+ is_expected.to contain_file("/custom/hooks/dir/@hashed/6e/40/6e4001871c0cf27c7634ef1dc478408f642410fd3a444e2a88e301f5c4a35a4d.git/custom_hooks/#{type}").
+ with_ensure('file').
+ with_content(content)
+ end
+ end
+
+ context "with type => #{type} and project id" do
+ let(:content) { "#!/usr/bin/env bash\ntest 0" }
+ let(:params) do
+ {
+ type: type,
+ repos_path: '/custom/hooks/dir',
+ content: content,
+ hashed_storage: true,
+ project: 93
+ }
+ end
+
+ it { is_expected.to compile }
+
+ it do
+ is_expected.to contain_file('/custom/hooks/dir/@hashed/6e/40/6e4001871c0cf27c7634ef1dc478408f642410fd3a444e2a88e301f5c4a35a4d.git/custom_hooks').
+ with_ensure('directory')
+ end
+
+ it do
+ is_expected.to contain_file("/custom/hooks/dir/@hashed/6e/40/6e4001871c0cf27c7634ef1dc478408f642410fd3a444e2a88e301f5c4a35a4d.git/custom_hooks/#{type}").
+ with_ensure('file').
+ with_content(content)
+ end
+ end
+ end
+end