I highly recommend using python-semantic-release instead. It provides full control over the release creation and does not have roadblocks in place preventing you from using it on non-latest commits. Still, if using semantic-release is a must, above scripts provide the means to do so.
-
-
Save c0m1c5an5/0a1315a135b37b00ba58d07881a1ec6a to your computer and use it in GitHub Desktop.
#!/usr/bin/env sh | |
# Copyright (c) 2024 Maksym Kondratenko | |
# SPDX-License-Identifier: BSD-3-Clause | |
set -eu | |
git_bin="/usr/bin/git" | |
get_remote() { | |
"${git_bin}" config --get remote.origin.url || : | |
} | |
get_branch() { | |
"${git_bin}" symbolic-ref -q --short HEAD || : | |
} | |
if ! [ -x "${git_bin}" ]; then | |
echo >&2 "ERR: Git binary '${git_bin}' not executable." | |
exit 127 | |
fi | |
if [ "${1:-}" == "ls-remote" ] && | |
[ "${2:-}" == "--heads" ]; then | |
if [ "${3:-}" == "$(get_remote)" ]; then | |
shift 3 | |
exec "${git_bin}" show-ref --heads "${@}" | |
fi | |
elif [ "${1:-}" == "push" ] && | |
[ "${2:-}" == "--dry-run" ] && | |
[ "${3:-}" == "--no-verify" ] && | |
[ "${4:-}" == "$(get_remote)" ] && | |
[ "${5:-}" == "HEAD:$(get_branch)" ]; then | |
exit 0 | |
fi | |
exec "${git_bin}" "${@}" |
# This snippet shows how to use the provided git wrapper | |
# to allow for execution when HEAD is behind remote branch. | |
mkdir -p ~/.local/opt/git-remote-mock | |
wget -O ~/.local/opt/git-remote-mock/git https://gist.githubusercontent.com/c0m1c5an5/0a1315a135b37b00ba58d07881a1ec6a/raw/07064587985e65d80fc3ee717551841330cdf832/git | |
chmod 755 ~/.local/opt/git-remote-mock/git | |
PATH="${HOME}/.local/opt/git-remote-mock:${PATH}" CI=true semantic-release |
It seems to be related to these lines:
/**
* Verify the write access authorization to remote repository with push dry-run.
*
* @param {String} repositoryUrl The remote repository URL.
* @param {String} branch The repository branch for which to verify write access.
* @param {Object} [execaOpts] Options to pass to `execa`.
*
* @throws {Error} if not authorized to push.
*/
export async function verifyAuth(repositoryUrl, branch, execaOptions) {
try {
await execa("git", ["push", "--dry-run", "--no-verify", repositoryUrl, `HEAD:${branch}`], execaOptions);
} catch (error) {
debug(error);
throw error;
}
}
https://github.com/semantic-release/semantic-release/blob/master/lib/git.js#L195C1-L211C2
@jaklan Does this version work for you?
#!/usr/bin/env sh
# Copyright (c) 2024 Maksym Kondratenko
# SPDX-License-Identifier: BSD-3-Clause
set -eu
git_bin="/usr/bin/git"
get_remote() {
"${git_bin}" config --get remote.origin.url || :
}
get_branch() {
"${git_bin}" symbolic-ref -q --short HEAD || :
}
if ! [ -x "${git_bin}" ]; then
echo >&2 "ERR: Git binary '${git_bin}' not executable."
exit 127
fi
if [ "${1:-}" == "ls-remote" ] &&
[ "${2:-}" == "--heads" ]; then
if [ "${3:-}" == "$(get_remote)" ]; then
shift 3
exec "${git_bin}" show-ref --heads "${@}"
fi
elif [ "${1:-}" == "push" ] &&
[ "${2:-}" == "--dry-run" ] &&
[ "${3:-}" == "--no-verify" ] &&
[ "${4:-}" == "$(get_remote)" ] &&
[ "${5:-}" == "HEAD:$(get_branch)" ]; then
exit 0
fi
exec "${git_bin}" "${@}"
@c0m1c5an5 I had to change it to:
elif [ "${1:-}" == "push" ] &&
[ "${2:-}" == "--dry-run" ] &&
[ "${3:-}" == "--no-verify" ]; then
exit 0
fi
but now works, thanks! I will test it further in CI now (and verify what was the issue with $4 and / or $5)
@jaklan It is most probable, that the $5 is different from what is expected.
Is the fifth argument to the wrapper call with [ "git", "--dry-run", "--no-verify", ... ] the same as the result of running git symbolic-ref -q --short HEAD
in your repository? (The strace command above outputs the info required).
Just as a follow-up - we have eventually decided to overwrite node_modules/semantic-release/lib/git.js
directly instead. We needed to change 2 functions:
- always return
true
inisBranchUpToDate()
:export async function isBranchUpToDate(repositoryUrl, branch, execaOptions) { return true; }
refs/heads/<placeholder>
instead ofbranch
inverifyAuth()
(maybe it could just be replaced with anothergit
function in the dry-run mode, but haven't tested):export async function verifyAuth(repositoryUrl, branch, execaOptions) { try { await execa( "git", ["push", "--dry-run", "--no-verify", repositoryUrl, `HEAD:refs/heads/placeholder`], execaOptions ); } catch (error) { debug(error); throw error; } }
The above could be also reflected in a git
script more or less this way:
#!/usr/bin/env bash
set -eu
git_bin="/usr/bin/git"
if ! [ -x "$git_bin" ]; then
echo >&2 "ERROR: '$git_bin' is not executable"
exit 127
fi
if [ "${1:-}" = "ls-remote" ] &&
[ "${2:-}" = "--heads" ] &&
[ -n "${3:-}" ] &&
[ -n "${4:-}" ]; then
exec $git_bin show-ref --heads "${4}"
elif [ "${1:-}" = "push" ] &&
[ "${2:-}" = "--dry-run" ] &&
[ "${3:-}" = "--no-verify" ] &&
[ -n "${4:-}" ]; then
exec $git_bin "${@:1:4}" "HEAD:refs/heads/placeholder"
else
exec $git_bin "${@}"
fi
Because we changed the approach - I haven't really tested that, but maybe could be helpful for others.
Okay, now I see the first
git
command being triggered is:and that already fails, before running any
git ls-remote
command. Tested with bothCI=true
and without.