commit 3574c3876c82b17c3fa04627511815090cf6d787
parent ebd2fb5f6ffbf4689b350948558856298b7eb31f
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 27 May 2026 17:19:14 +0200
git-publish: major refactoring
This refactoring was initiated to improve the readability of the script,
whose behavior had become more complex. The goal was to help enhance
robustness.
As a result, this rewrite has made it possible to account for edge cases
that were previously ignored and to standardize error handling.
processing of a repository fails. From now on, an error is returned and
the command stops as soon as the processing of a repository fails. This
could complicate the management of a set of repositories, for example
when disabling the publication of all repositories in a directory (i.e.,
*.git) even though some of them were not published. This is no longer
possible, and you must specify the exact list of published repositories.
If this makes the process so cumbersome that it becomes a problem, an
option could be added to allow processing of subsequent repositories to
continue.
"repo" is no longer a global variable. The helper functions that rely on
it now take its value as their first argument. This applies to the
function that checks whether the path corresponds to a bare repository,
as well as to the one that checks whether the repository is blocked from
publishing.
It was difficult to determine whether a variable was local or global.
Global variables are now all declared outside of functions, at the very
beginning of the file. Variables local to a function are prefixed with
an underscore.
Diffstat:
| M | git-publish | | | 360 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
1 file changed, 196 insertions(+), 164 deletions(-)
diff --git a/git-publish b/git-publish
@@ -30,6 +30,23 @@ else
GIT_PUBLISH_RESOURCES_PATH="@RESOURCES_PATH@"
fi
+base_url="${GIT_PUBLISH_BASE_URL:-}"
+dir_git="${GIT_PUBLISH_DIR_GIT:-/srv/git}"
+dir_www="${GIT_PUBLISH_DIR_WWW:-/srv/www/git}"
+force=0 # Force HTML generation
+delete=0 # Delete publication
+
+# Move the repository to the publication directory instead of creating a
+# link to it there. The original repository then becomes a symbolic link
+# to the published repository.
+mv_repo=0
+
+hook="${GIT_PUBLISH_RESOURCES_PATH}/post-receive.in"
+
+# Setup the hook header: it identifies the hook
+digest="$(cksum "${hook}" | cut -d' ' -f1)"
+header='# git-publish '"${digest}"
+
########################################################################
# Helper functions
########################################################################
@@ -84,31 +101,37 @@ check_directory() # path
cd "${OLDPWD}"
}
-# Inputs:
-# - repo: git bare repository
-check_repo()
+check_repo() # repo
{
- cd "${repo}"
+ _err=0
- if ! is_bare_repo="$(git rev-parse --is-bare-repository 2> /dev/null)" \
- || [ "${is_bare_repo}" = "false" ]; then
- >&2 printf 'not a git bare repository\n'
- return 1
+ if [ ! -e "$1" ] || [ -f "$1" ]; then
+ _err=1
+ else
+ cd "$1"
+
+ if ! _is_bare_repo="$(git rev-parse --is-bare-repository 2> /dev/null)" \
+ || [ "${_is_bare_repo}" = "false" ]; then
+ _err=1;
+ fi
+
+ cd "${OLDPWD}"
fi
- cd "${OLDPWD}"
+ if [ "${_err}" -ne 0 ]; then
+ >&2 printf '%s: not a git bare repository\n' "$1"
+ return 1
+ fi
}
-# Inputs:
-# - repo: git bare repository (absolute path)
-publication_ban()
+publication_ban() # repo
{
- cd -- "${repo}"
+ cd -- "$1"
# Retrieve the directory where git files are stored
if ! git_dir=$(git rev-parse --path-format=absolute --git-dir 2>&1)
then
- >&2 printf '%s: %s\n' "${repo}" "${git_dir}"
+ >&2 printf '%s: %s\n' "$1" "${git_dir}"
die
fi
@@ -122,101 +145,33 @@ publication_ban()
fi
}
-# Inputs:
-# - base_url: base URL under which the git HTML repository is exposed
-# - dir_git: directory where to publish the git repository
-# - dir_www: directory where to publish the git repository's HTML pages
-# - repo: git bare repository
-# - force: force generation of HTML pages from scratch
-# - mv_repo: reverse the symbolic link
-publish_repo()
-{
- repo_name=$(basename "${repo}" ".git")
- repo_git="${dir_git}/${repo_name}.git"
-
- if [ -e "${repo_git}" ]; then
- # If the path to the published repository already exists,
- # verify that it matches the repository to publish...
- if [ ! -f "${repo_git}" ]; then
- dir0="$(cd "${repo_git}" && pwd -P)"
- fi
- dir1="$(cd "${repo}" && pwd -P)"
- if [ "${dir0}" != "${dir1}" ]; then
- >&2 printf \
- '"%s" already exists and is not the public version of "%s"\n' \
- "${repo_git}" "${repo}"
- die 1
- fi
-
- # ... and make sure that the source path points to a directory and
- # not a symbolic link. The “mv_repo” option is discussed below
- if [ -L "${repo}" ]; then
- rm "${repo}"
- mv "${repo_git}" "${repo}"
- fi
-
- # Finally, delete the symbolic link in the public directory,
- # if it exists.
- rm -f "${repo_git}"
- fi
-
- # Publish the git repository, i.e., create a symbolic link to it in
- # the publicly accessible directory, or move it to the public
- # directory and create a symbolic link from the original path to its
- # public version.
- if [ "${mv_repo}" -eq 0 ]; then
- ln -s "${repo}" "${dir_git}"
- else
- mv "${repo}" "${dir_git}"
- ln -s "${repo_git}" "$(dirname "${repo}")"
- fi
-
- # Create directory publicly served by the WWW daemon
- repo_www="${dir_www}/${repo_name}"
- [ "${force}" -ne 0 ] && rm -rf "${repo_www}"
- mkdir -p "${repo_www}"
-
- # Generate HTML pages for the repository to be published
- # Make sure the links are relative to the repository directory to
- # avoid problems on the web server when it chroots
- cd "${repo_www}"
- stagit -c .cache -u "${base_url}/${repo_name}/" "${repo_git}"
- ln -sf './log.html' ./index.html
- ln -sf '../style.css' ./style.css
- ln -sf '../logo.png' ./logo.png
- ln -sf '../favicon.png' ./favicon.png
- cd "${OLDPWD}"
-}
-
# Returns 0 if the repository has no receive hook or if it is the one
# configured by git-publish, and >0 otherwise.
# Inputs:
# - 1: repository
+# - header: hook magic cookie
+# - hook: path to the hook template
check_post_receive_hook()
{
- hook="${GIT_PUBLISH_RESOURCES_PATH}/post-receive.in"
-
- # Setup the hook header: it identifies the hook
- digest="$(cksum "${hook}" | cut -d' ' -f1)"
- header='# git-publish '"${digest}"
-
if [ -e "$1/hooks/post-receive" ]; then
- header2="$(sed -n '2p' "$1/hooks/post-receive")"
- if [ "${header}" != "${header2}" ]; then
+ _header2="$(sed -n '2p' "$1/hooks/post-receive")"
+ if [ "${header}" != "${_header2}" ]; then
return 1
fi
fi
}
# Inputs:
+# - 1: git bare repository
# - base_url: base URL under which the git HTML repository is exposed
# - dir_git: directory where to publish the git repository
# - dir_www: directory where to publish the git repository's HTML pages
-# - repo: git bare repository
+# - header: hook magic cookie
+# - hook: path to the hook template
setup_post_receive_hook()
{
# shellcheck disable=SC2310
- if ! check_post_receive_hook "${repo}"; then
+ if ! check_post_receive_hook "$1"; then
# Don't overwrite the repository's already configured post-receive
# hook if it hasn't been configured by git-publish.
>&2 printf 'another post-receive hook already exist\n'
@@ -227,9 +182,9 @@ setup_post_receive_hook()
| sed -e "s#@DIR_GIT@#${dir_git}#g" \
-e "s#@DIR_WWW@#${dir_www}#g" \
-e "s#@BASE_URL@#${base_url}#g" \
- > "${repo}/hooks/post-receive"
+ > "$1/hooks/post-receive"
- chmod 755 "${repo}/hooks/post-receive"
+ chmod 755 "$1/hooks/post-receive"
}
# Create an index from the list of directories in 'dir_www' that
@@ -240,16 +195,16 @@ setup_post_receive_hook()
# - dir_www: directory where to publish the git repository's HTML pages
make_index()
{
- tmpfile="${TMPDIR:-/tmp}/git-publish-index.txt"
+ _tmpfile="${TMPDIR:-/tmp}/git-publish-index.txt"
# Removes trailing slashes. This allows you to write the following
# regular expressions for find directives
- dir=$(dirname "${dir_www}")
- www=$(basename "${dir_www}")
- dir_www="${dir}/${www}"
- dir=$(dirname "${dir_git}")
- git=$(basename "${dir_git}")
- dir_git="${dir}/${git}"
+ _dir=$(dirname "${dir_www}")
+ _www=$(basename "${dir_www}")
+ dir_www="${_dir}/${_www}"
+ _dir=$(dirname "${dir_git}")
+ _git=$(basename "${dir_git}")
+ dir_git="${_dir}/${_git}"
# Build list of candidate git repositories from the directories of the
# publicly exposed WWW directory
@@ -258,56 +213,174 @@ make_index()
printf '%s\n' \"\$@\" \
| sed 's;${dir_www}/\(.\{1,\}\)$;${dir_git}/\1.git;' \
| sort" \
- -- {} + > "${tmpfile}"
+ -- {} + > "${_tmpfile}"
# Compare the candidate list to the list of publicly exposed git
# repositories. The intersection corresponds to the repositories to
# exposed in the HTML index
- repo_list=$(find "${dir_git}" -path "${dir_git}/*.git" -prune | sort \
- | join - "${tmpfile}" | tr '\n' ' ')
+ _repo_list=$(find "${dir_git}" -path "${dir_git}/*.git" -prune | sort \
+ | join - "${_tmpfile}" | tr '\n' ' ')
- if [ -z "${repo_list}" ]; then
+ if [ -z "${_repo_list}" ]; then
# No repo to index. Delete index file if any
rm -f "${dir_www}/index.html"
else
# Generate the index
# shellcheck disable=SC2086
- stagit-index ${repo_list} > "${dir_www}/index.html"
+ stagit-index ${_repo_list} > "${dir_www}/index.html"
fi
- rm -f "${tmpfile}"
+ rm -f "${_tmpfile}"
}
# Inputs:
-# - @: repository list
+# - 1: git bare repository
# - base_url: base URL under which the git HTML repository is exposed
# - dir_git: directory where to publish the git repository
# - dir_www: directory where to publish the git repository's HTML pages
# - force: force generation of HTML pages from scratch
-publish() # list of repositories
+# - mv_repo: reverse the symbolic link
+publish_repo()
{
- printf '%s\n' "$@" | while read -r repo; do
- # Make the repository path absolute to ensure both the validity of
- # the symbolic link and a valid repository name
- repo="$(cd -- "${repo}" && echo "${PWD}")"
+ # shellcheck disable=SC2310
+ check_repo "$1" || return 1
+
+ # Make the repository path absolute to ensure both the validity of
+ # the symbolic link and a valid repository name
+ _repo="$(cd -- "$1" && echo "${PWD}")"
- printf '%s: ' "${repo}"
+ # Isn't the repository prohibited from publication?
+ # shellcheck disable=SC2310
+ if publication_ban "${_repo}"; then
+ printf '%s: BAN\n' "${_repo}"
+ return 0
+ fi
- # Isn't the repository prohibited from publication?
+ _repo_name=$(basename "${_repo}" ".git")
+ _repo_git="${dir_git}/${_repo_name}.git"
+
+ if [ -e "${_repo_git}" ]; then
# shellcheck disable=SC2310
- if publication_ban "${repo}"; then
- printf 'ban\n'
-
- else
- check_repo
- publish_repo
- setup_post_receive_hook
- printf 'done\n'
+ check_repo "${_repo_git}" || return 1
+
+ # If the path to the published repository already exists,
+ # verify that it matches the repository to publish...
+ _dir0="$(cd "${_repo_git}" && pwd -P)"
+ _dir1="$(cd "${_repo}" && pwd -P)"
+ if [ "${_dir0}" != "${_dir1}" ]; then
+ >&2 printf \
+ '"%s" already exists and is not the public version of "%s"\n' \
+ "${_repo_git}" "${_repo}"
+ return 1
fi
+
+ # ... and make sure that the source path points to a directory and
+ # not a symbolic link. The "mv_repo" option is discussed below.
+ if [ -L "${_repo}" ]; then
+ rm "${_repo}"
+ mv "${_repo_git}" "${_repo}"
+ fi
+
+ # Finally, delete the symbolic link in the public directory,
+ # if it exists.
+ rm -f "${_repo_git}"
+ fi
+
+ # Publish the git repository, i.e., create a symbolic link to it in
+ # the publicly accessible directory, or move it to the public
+ # directory and create a symbolic link from the original path to its
+ # public version.
+ if [ "${mv_repo}" -eq 0 ]; then
+ ln -s "${_repo}" "${dir_git}"
+ else
+ mv "${_repo}" "${dir_git}"
+ ln -s "${_repo_git}" "$(dirname "${_repo}")"
+ fi
+
+ # Create directory publicly served by the WWW daemon
+ _repo_www="${dir_www}/${_repo_name}"
+ [ "${force}" -ne 0 ] && rm -rf "${_repo_www}"
+ mkdir -p "${_repo_www}"
+
+ # Generate HTML pages for the repository to be published
+ # Make sure the links are relative to the repository directory to
+ # avoid problems on the web server when it chroots
+ cd "${_repo_www}"
+ stagit -c .cache -u "${base_url}/${_repo_name}/" "${_repo_git}"
+ ln -sf './log.html' ./index.html
+ ln -sf '../style.css' ./style.css
+ ln -sf '../logo.png' ./logo.png
+ ln -sf '../favicon.png' ./favicon.png
+ cd "${OLDPWD}"
+
+ setup_post_receive_hook "${_repo}"
+}
+
+# Inputs:
+# - @: repository list
+# - base_url: base URL under which the git HTML repository is exposed
+# - dir_git: directory where to publish the git repository
+# - dir_www: directory where to publish the git repository's HTML pages
+# - force: force generation of HTML pages from scratch
+publish() # list of repositories
+{
+ printf '%s\n' "$@" | while read -r _i; do
+ publish_repo "${_i}"
done
}
# Inputs:
+# - dir_git: directory where to publish the git repositories
+# - dir_www: directory where to publish the git repositories' HTML pages
+# - repo: git bare repository
+unpublish_repo()
+{
+ # shellcheck disable=SC2310
+ check_repo "$1" || return 1
+
+ # Make the repository path absolute to ensure both the validity of
+ # the symbolic link and a valid repository name
+ _repo="$(cd -- "$1" && echo "${PWD}")"
+ printf '%s: ' "${_repo}"
+
+ _repo_name=$(basename "${_repo}" ".git")
+ _repo_www="${dir_www}/${_repo_name}"
+ _repo_git="${dir_git}/${_repo_name}.git"
+
+ # shellcheck disable=SC2310
+ check_repo "${_repo_git}" || return 1;
+
+ # Make sure that both paths point to the same physical repository.
+ # In other words, that one is a symbolic link to the other
+ _dir0="$(cd "${_repo}" && pwd -P)"
+ _dir1="$(cd "${_repo_git}" && pwd -P)"
+ if [ "${_dir0}" != "${_dir1}" ]; then
+ >&2 printf 'not published\n'
+ return 1
+ fi
+
+ # Remove publicly exposed directory
+ if [ -L "${_repo_git}" ]; then
+ rm "${_repo_git}"
+ elif [ -L "${_repo}" ]; then # rev_symlink
+ rm "${_repo}";
+ mv "${_repo_git}" "$(dirname "${_repo}")"
+ else
+ >&2 printf 'Unexpected error\n' # This should not happen
+ die 1
+ fi
+
+ rm -rf "${_repo_www}" # Remove HTML pages
+
+ # shellcheck disable=SC2310
+ if check_post_receive_hook "${_repo}"; then
+ rm -f "${_repo}/hooks/post-receive"
+ fi
+
+ printf 'done\n'
+}
+
+# Inputs:
# - @ : repository list
# - base_url: base URL under which the git HTML repositories are exposed
# - dir_git: directory where to publish the git repositories
@@ -315,55 +388,14 @@ publish() # list of repositories
# - force: force generation of HTML pages from scratch
unpublish()
{
- printf '%s\n' "$@" | while read -r repo; do
- # Make the repository path absolute to ensure both the validity of
- # the symbolic link and a valid repository name
- repo="$(cd -- "${repo}" && echo "${PWD}")"
- printf '%s: ' "${repo}"
-
- check_repo
-
- repo_name=$(basename "${repo}" ".git")
- repo_www="${dir_www}/${repo_name}"
- repo_git="${dir_git}/${repo_name}.git"
-
- # Remove publicly exposed directory
- if [ -L "${repo_git}" ]; then
- rm "${repo_git}"
- elif [ -L "${repo}" ]; then # rev_symlink
- rm "${repo}";
- mv "${repo_git}" "$(dirname "${repo}")"
- else
- >&2 printf 'neither %s nor %s is a symlink\n' \
- "${repo}" "${repo_git}"
- continue
- fi
-
- rm -rf "${repo_www}" # Remove HTML pages
-
- # shellcheck disable=SC2310
- if check_post_receive_hook "${repo}"; then
- rm -f "${repo}/hooks/post-receive"
- fi
-
- printf 'done\n'
+ printf '%s\n' "$@" | while read -r _i; do
+ unpublish_repo "${_i}"
done
}
########################################################################
# The script
########################################################################
-base_url="${GIT_PUBLISH_BASE_URL:-}"
-dir_git="${GIT_PUBLISH_DIR_GIT:-/srv/git}"
-dir_www="${GIT_PUBLISH_DIR_WWW:-/srv/www/git}"
-force=0 # Force HTML generation
-delete=0 # Delete publication
-
-# Move the repository to the publication directory instead of creating a
-# link to it there. The original repository then becomes a symbolic link
-# to the published repository.
-mv_repo=0
-
# Parse input arguments
OPTIND=1
while getopts ":dfg:mu:w:" opt; do