Files
fish-shell/build_tools/release.sh
Daniel Rainer 20cc07c5cd xtask: add html-docs task
This task is a bit annoying to implement because `sphinx-build` depends
on `fish_indent`, so that needs to be built, and the binary location
added to `PATH`.

Building `fish_indent` can be avoided by setting the `--fish-indent`
argument to the path to an existing `fish_indent` executable.

Use the new xtask in CMake builds. To do so without having to add
configuration options about where to put the docs, or having to copy
them from the default location to `build/user_doc`, we instead get rid of
the `user_doc` directory and change the code which accesses the docs to
read them from the non-configurable default output location in Cargo's
target directory.

Part of #12292
2026-01-21 17:43:26 +01:00

313 lines
7.6 KiB
Bash
Executable File

#!/bin/sh
{
set -ex
version=$1
repository_owner=fish-shell
remote=origin
if [ -n "$2" ]; then
set -u
repository_owner=$2
remote=$3
set +u
[ $# -eq 3 ]
fi
[ -n "$version" ]
for tool in \
cmake \
bundle \
diff \
gh \
gpg \
jq \
ninja \
ruby \
tar \
timeout \
uv \
xz \
; do
if ! command -v "$tool" >/dev/null; then
echo >&2 "$0: missing command: $1"
exit 1
fi
done
committer=$(git var GIT_AUTHOR_IDENT)
committer=${committer% *} # strip timezone
committer=${committer% *} # strip timestamp
gpg --local-user="$committer" --sign </dev/null >/dev/null
repo_root="$(dirname "$0")/.."
fish_site=$repo_root/../fish-site
fish_site_repo=git@github.com:$repository_owner/fish-site
for path in . "$fish_site"
do
if ! git -C "$path" diff HEAD --quiet ||
git ls-files --others --exclude-standard | grep .; then
echo >&2 "$0: index and worktree must be clean"
exit 1
fi
done
(
cd "$fish_site"
[ "$(git rev-parse HEAD)" = \
"$(git ls-remote "$fish_site_repo" refs/heads/master |
awk '{print $1}')" ]
)
if git tag | grep -qxF "$version"; then
echo >&2 "$0: tag $version already exists"
exit 1
fi
integration_branch=$(
git for-each-ref --points-at=HEAD 'refs/heads/Integration_*' \
--format='%(refname:strip=2)'
)
[ -n "$integration_branch" ] ||
git merge-base --is-ancestor $remote/master HEAD
sed -n 1p CHANGELOG.rst | grep -q '^fish .*(released .*)$'
sed -n 2p CHANGELOG.rst | grep -q '^===*$'
changelog_title="fish $version (released $(date +'%B %d, %Y'))"
sed -i \
-e "1c$changelog_title" \
-e "2c$(printf %s "$changelog_title" | sed s/./=/g)" \
CHANGELOG.rst
CreateCommit() {
git commit -m "$2
Created by ./build_tools/release.sh $version"
}
sed -i "s/^version = \".*\"/version = \"$1\"/g" Cargo.toml
cargo fetch --offline
if [ "$1" = "$version" ]; then
# debchange is a Debian script to manage the Debian changelog, but
# it's too annoying to install everywhere. Just do it by hand.
cat - contrib/debian/changelog > contrib/debian/changelog.new <<EOF
fish (${version}-1) stable; urgency=medium
* Release of new version $version.
See https://github.com/fish-shell/fish-shell/releases/tag/$version for details.
-- $committer $(date -R)
EOF
mv contrib/debian/changelog.new contrib/debian/changelog
git add contrib/debian/changelog
fi
git add CHANGELOG.rst Cargo.toml Cargo.lock
CreateCommit "Release $version"
git -c "user.signingKey=$committer" \
tag --sign --message="Release $version" $version
git push $remote $version
TIMEOUT=
gh() {
command ${TIMEOUT:+timeout $TIMEOUT} \
gh --repo "$repository_owner/fish-shell" "$@"
}
gh workflow run release.yml --ref="$version" \
--raw-field="version=$version"
run_id=
while [ -z "$run_id" ] && sleep 5
do
run_id=$(gh run list \
--json=databaseId --jq=.[].databaseId \
--workflow=release.yml --limit=1 \
--commit="$(git rev-parse "$version^{commit}")")
done
# Update fishshell.com
tag_oid=$(git rev-parse "$version")
tmpdir=$(mktemp -d)
fish_tar_xz=fish-$version.tar.xz
(
local_tarball=$tmpdir/local-tarball
mkdir "$local_tarball"
FISH_ARTEFACT_PATH=$local_tarball ./build_tools/make_tarball.sh
cd "$local_tarball"
tar xf "$fish_tar_xz"
)
# TODO This works on draft releases only if "gh" is configured to
# have write access to the fish-shell repository. Unless we are fine
# publishing the release at this point, we should at least fail if
# "gh" doesn't have write access.
while ! \
gh release download "$version" --dir="$tmpdir" \
--pattern="$fish_tar_xz"
do
TIMEOUT=30 gh run watch "$run_id" ||:
sleep 5
done
actual_tag_oid=$(git ls-remote "$remote" |
awk '$2 == "refs/tags/'"$version"'" { print $1 }')
[ "$tag_oid" = "$actual_tag_oid" ]
(
cd "$tmpdir"
tar xf "$fish_tar_xz"
diff -ur "fish-$version" "local-tarball/fish-$version"
gpg --local-user="$committer" --sign --detach --armor \
"$fish_tar_xz"
gh release upload "$version" "$fish_tar_xz.asc"
)
(
cd "$tmpdir/local-tarball/fish-$version"
uv --no-managed-python venv
. .venv/bin/activate
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug .
ninja doc
)
CopyDocs() {
rm -rf "$fish_site/site/docs/$1"
cp -r "$tmpdir/local-tarball/fish-$version/cargo/fish-docs/html" "$fish_site/site/docs/$1"
git -C $fish_site add "site/docs/$1"
}
minor_version=${version%.*}
CopyDocs "$minor_version"
latest_release=$(
releases=$(git tag | grep '^[0-9]*\.[0-9]*\.[0-9]*.*' |
sed $(: "De-prioritize release candidates (1.2.3-rc0)") \
's/-/~/g' | LC_ALL=C sort --version-sort)
printf %s\\n "$releases" | tail -1
)
if [ "$version" = "$latest_release" ]; then
CopyDocs current
fi
rm -rf "$tmpdir"
(
cd "$fish_site"
make
git add -u
git add docs
if git ls-files --others --exclude-standard | grep .; then
exit 1
fi
git commit --message="$(printf %s "\
| Release $version (docs)
|
| Created by ../fish-shell/build_tools/release.sh
" | sed 's,^\s*| \?,,')"
)
gh_api_repo() {
path=$1
shift
command gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$repository_owner/fish-shell/$path" \
"$@"
}
# Approve macos-codesign
# TODO what if current user can't approve?
gh_pending_deployments() {
gh_api_repo "actions/runs/$run_id/pending_deployments" "$@"
}
while {
environment_id=$(gh_pending_deployments | jq .[].environment.id)
[ -z "$environment_id" ]
}
do
sleep 5
done
echo '
{
"environment_ids": ['"$environment_id"'],
"state": "approved",
"comment": "Approved via ./build_tools/release.sh"
}
' |
gh_pending_deployments --method POST --input=-
# Await completion.
gh run watch "$run_id"
while {
! draft=$(gh release view "$version" --json=isDraft --jq=.isDraft) \
|| [ "$draft" = true ]
}
do
sleep 20
done
(
cd "$fish_site"
make new-release
git add -u
git add docs
if git ls-files --others --exclude-standard | grep .; then
exit 1
fi
git commit --message="$(printf %s "\
| Release $version (release list update)
|
| Created by ../fish-shell/build_tools/release.sh
" | sed 's,^\s*| \?,,')"
# This takes care to support remote names that are different from
# fish-shell remote name. Also, support detached HEAD state.
git push "$fish_site_repo" HEAD:master
git fetch "$fish_site_repo" \
"$(git rev-parse HEAD):refs/remotes/origin/master"
)
if [ -n "$integration_branch" ]; then {
git push $remote "$version^{commit}":refs/heads/$integration_branch
} else {
changelog=$(cat - CHANGELOG.rst <<EOF
fish ?.?.? (released ???)
=========================
EOF
)
printf %s\\n "$changelog" >CHANGELOG.rst
git add CHANGELOG.rst
CreateCommit "start new cycle"
git push $remote HEAD:master
} fi
milestone_version="$(
if echo "$version" | grep -q '\.0$'; then
echo "$minor_version"
else
echo "$version"
fi
)"
milestone_number() {
gh_api_repo milestones?state=open |
jq --arg name "fish $1" '
.[] | select(.title == $name) | .number
'
}
gh_api_repo milestones/"$(milestone_number "$milestone_version")" \
--method PATCH --raw-field state=closed
next_minor_version=$(echo "$minor_version" |
awk -F. '{ printf "%s.%s", $1, $2+1 }')
if [ -z "$(milestone_number "$next_minor_version")" ]; then
gh_api_repo milestones --method POST \
--raw-field title="fish $next_minor_version"
fi
exit
}