# # Release script library # # Copyright (c) 2012-2022 Michael Buesch # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # default_hook_pre_checkout() { true } default_hook_post_checkout() { cd "$1" find "$1" \( \ \( -name 'makerelease*' \) -o \ \( -name '.git*' \) \ \) -print0 | xargs -0 rm -r } default_hook_pre_relocate_checkout() { true } default_hook_post_relocate_checkout() { true } default_hook_pre_documentation() { true } default_hook_post_documentation() { true } default_hook_pre_archives() { true } default_hook_post_archives() { true } default_hook_pre_doc_archives() { true } default_hook_doc_archives() { info "By default no documentation archives are created." info "Please override hook_doc_archives." } default_hook_post_doc_archives() { true } default_hook_testbuild() { cd "$1" if ! echo "$conf_testbuild" | grep -q no-configure &&\ [ -x ./configure ]; then ./configure fi if ! echo "$conf_testbuild" | grep -q no-cmake &&\ [ -r ./CMakeLists.txt ]; then cmake . fi if ! echo "$conf_testbuild" | grep -q no-make &&\ [ -r ./GNUmakefile -o -r ./makefile -o -r ./Makefile ]; then make fi if ! echo "$conf_testbuild" | grep -q no-setuppy &&\ [ -x ./setup.py ]; then ./setup.py --no-user-cfg build fi if [ -r ./Cargo.toml ]; then if ! echo "$conf_testbuild" | grep -q no-cargo-build; then info "Running cargo DEBUG build" do_cargo build info "Running cargo RELEASE build" do_cargo build --release fi if ! echo "$conf_testbuild" | grep -q no-cargo-examples; then info "Running cargo EXAMPLES build" do_cargo build --examples info "Running cargo EXAMPLES RELEASE build" do_cargo build --examples --release fi if ! echo "$conf_testbuild" | grep -q no-cargo-clippy; then info "Running cargo CLIPPY" do_cargo clippy -- --deny warnings if ! echo "$conf_testbuild" | grep -q no-cargo-tests; then info "Running cargo CLIPPY on tests" do_cargo clippy --tests -- --deny warnings fi if ! echo "$conf_testbuild" | grep -q no-cargo-examples; then info "Running cargo CLIPPY on examples" do_cargo clippy --examples -- --deny warnings fi fi if ! echo "$conf_testbuild" | grep -q no-cargo-audit; then info "Running cargo AUDIT" do_cargo audit --deny warnings fi fi } default_hook_regression_tests() { cd "$1" if [ -r ./Cargo.toml ]; then do_cargo test do_cargo test --examples fi } default_hook_pre_archive_signatures() { true } default_hook_post_archive_signatures() { true } default_hook_pre_tag() { true } default_hook_post_tag() { true } default_hook_pre_move_files() { true } default_hook_post_move_files() { true } default_hook_version() { cd "$1" if [ -r "./$conf_package/Cargo.toml" ]; then version="$(grep -Ee '^version\s+=\s+' "./$conf_package/Cargo.toml" | head -n1 | cut -d'"' -f2)" else die "ERROR: Must supply hook_version()" fi } default_hook_pre_upload() { true } default_hook_post_upload() { true } default_hook_pre_debian_packages() { true } default_hook_post_debian_packages() { true } # Assign default hooks to actual hooks hook_pre_checkout() { default_hook_pre_checkout "$@"; } hook_post_checkout() { default_hook_post_checkout "$@"; } hook_pre_relocate_checkout() { default_hook_pre_relocate_checkout "$@"; } hook_post_relocate_checkout() { default_hook_post_relocate_checkout "$@"; } hook_pre_documentation() { default_hook_pre_documentation "$@"; } hook_post_documentation() { default_hook_post_documentation "$@"; } hook_pre_archives() { default_hook_pre_archives "$@"; } hook_post_archives() { default_hook_post_archives "$@"; } hook_pre_doc_archives() { default_hook_pre_doc_archives "$@"; } hook_doc_archives() { default_hook_doc_archives "$@"; } hook_post_doc_archives() { default_hook_post_doc_archives "$@"; } hook_testbuild() { default_hook_testbuild "$@"; } hook_regression_tests() { default_hook_regression_tests "$@"; } hook_pre_archive_signatures() { default_hook_pre_archive_signatures "$@"; } hook_post_archive_signatures() { default_hook_post_archive_signatures "$@"; } hook_pre_tag() { default_hook_pre_tag "$@"; } hook_post_tag() { default_hook_post_tag "$@"; } hook_pre_move_files() { default_hook_pre_move_files "$@"; } hook_post_move_files() { default_hook_post_move_files "$@"; } hook_get_version() { default_hook_version "$@"; } hook_pre_upload() { default_hook_pre_upload "$@"; } hook_post_upload() { default_hook_post_upload "$@"; } hook_pre_debian_packages() { default_hook_pre_debian_packages "$@"; } hook_post_debian_packages() { default_hook_post_debian_packages "$@"; } cleanup() { [ -d "$tmpdir" ] && { if [ $opt_keeptmp -eq 0 ]; then rm -rf "$tmpdir" else info "Keeping temporary directory '$tmpdir' in place." fi } } # $1=code abort() { cleanup exit $1 } # $*=message die() { echo "$*" abort 1 } terminating_signal() { die "Terminating signal received" } # $*=message info() { echo "--- $*" } # $*=message warn() { echo "--- WARNING: $*" >&2 } is_dry_run() { [ $opt_dryrun -ne 0 ] } dry_run_prefix() { is_dry_run && echo -n "echo dry-run " || true } # $1=program_name, $2+=program_args dry_run() { $(dry_run_prefix) "$@" } # $1=program_name have_program() { which "$1" >/dev/null 2>&1 } # $1=program_name, ($2=description) assert_program() { local bin="$1" local desc="$2" [ -n "$desc" ] || desc="$bin" have_program "$bin" || die "$bin not found. Please install $desc." } # $1=path_to_git_repo git_main_branch() { if GIT_DIR="$1" git branch | grep -qE '^\*? ?main$'; then echo "main" else echo "master" fi } # $1=hook_name, $2+=hook_parameters execute_hook() { local hook_name="hook_$1" local oldpwd="$(pwd)" shift set -e eval $hook_name "$@" set +e [ "$(pwd)" != "$oldpwd" ] && { cd "$oldpwd" || die "execute_hook: Failed to switch back to old PWD '$oldpwd'" } } maketemp() { local suffix="$1" mktemp --suffix="$suffix" --tmpdir="$tmpdir" "makerelease-tmpfile.XXXXXXXX" } detect_repos_type() { [ -z "$repos_type" -a -d "$srcdir/.git" ] && repos_type=git [ -z "$repos_type" ] && repos_type=none case "$repos_type" in none|git) ;; # ok *) die "Invalid \$repos_type=$repos_type" ;; esac } do_cargo() { local command="$1" shift local opt_package= if [ "$command" != "audit" ]; then if [ -n "$conf_package" ]; then local opt_package="--package $conf_package" fi fi info cargo "$command" $opt_package "$@" cargo "$command" $opt_package "$@" } make_checkout() { checkout_dir="$tmpdir/$project-checkout" mkdir -p "$checkout_dir" || die "Failed to make checkout dir" execute_hook pre_checkout "$checkout_dir" case "$repos_type" in none) info "Copying source tree" cp -r "$srcdir" "$checkout_dir" || \ die "Failed to copy source tree" ;; git) info "Creating git checkout" assert_program git local branch="$(git_main_branch "$srcdir/.git")" [ -n "$opt_ref" ] && branch="$opt_ref" export GIT_DIR="$checkout_dir/.git" git clone --recursive --shared --no-checkout \ "$srcdir/.git" "$checkout_dir" || \ die "Failed to clone git repository" cd "$checkout_dir" || die "Internal error: cd" git checkout -b "__tmp_makerelease-$branch" "$branch" || \ die "git checkout failed (1)" git checkout -f || \ die "git checkout failed (2)" if [ -f "$checkout_dir/.gitmodules" ]; then git submodule update --init --recursive || \ die "git submodule update failed." fi ;; *) die "checkout: Unknown repos_type" ;; esac execute_hook post_checkout "$checkout_dir" } detect_versioning() { version= release_name= execute_hook get_version "$checkout_dir/$srcsubdir" [ -n "$(echo "$version" | tr -d '.[:blank:]')" ] ||\ die "\$version not set correctly in hook_get_version()" version="${version}${opt_extraversion}" [ -n "$release_name" ] || release_name="$project-$version" } relocate_checkout() { execute_hook pre_relocate_checkout "$tmpdir" "$checkout_dir" local new_checkout_dir="$tmpdir/$release_name" mv "$checkout_dir/$srcsubdir" "$new_checkout_dir" || \ die "Failed to relocate checkout" rm -rf "$checkout_dir" checkout_dir="$new_checkout_dir" execute_hook post_relocate_checkout "$tmpdir" "$checkout_dir" } make_debian_packages() { [ $opt_nodebian -eq 0 ] || return [ $opt_nobuild -eq 0 ] || return [ -d "$checkout_dir/debian" ] || return info "Creating Debian packages" have_program debuild || { warn "No 'debuild' available. Skipping Debian build." return } grep -qe quilt "$checkout_dir/debian/source/format" &&\ die "Debian package format 'quilt' is not supported." local ver_without_suffix="$(printf '%s' "$version" | grep -oEe '[^\-]+' | head -n1)" grep -qEe "${project}"'\s*\(\s*'"${ver_without_suffix}" \ "$checkout_dir/debian/changelog" ||\ die "Debian changelog does not contain the version $version." local debuild_dir="${checkout_dir}_debuild" cp -a "$checkout_dir" "$debuild_dir" ||\ die "Failed to copy checkout_dir for debuild." execute_hook pre_debian_packages "$debuild_dir" cd "$debuild_dir" || die "Failed to cd to debuild_dir" local debuild_opts= if [ $opt_nosign -eq 0 -a -n "$DEB_SIGN_KEYID" ]; then info "(Signing Debian packages with key '$DEB_SIGN_KEYID', from DEB_SIGN_KEYID)" local debuild_opts="-k$DEB_SIGN_KEYID" elif [ $opt_nosign -eq 0 -a -n "$GPG_KEY_RELEASE" ]; then info "(Signing Debian packages with key '$GPG_KEY_RELEASE', from GPG_KEY_RELEASE)" local debuild_opts="-k$GPG_KEY_RELEASE" else info "(Creating unsigned Debian packages)" local debuild_opts="-uc -us" fi CFLAGS= CPPFLAGS= CXXFLAGS= LDFLAGS= debuild $debuild_opts ||\ die "Failed to build debian package" local deb_archive_dir="$archive_dir/debian" mkdir -p "$deb_archive_dir" ||\ die "Failed to create Debian archive directory" mv "$debuild_dir/../"*.deb "$deb_archive_dir/" ||\ die "Failed to move .deb files to archive directory" mv "$debuild_dir/../"*.dsc "$deb_archive_dir/" ||\ die "Failed to move .dsc files to archive directory" mv "$debuild_dir/../"*.changes "$deb_archive_dir/" ||\ die "Failed to move .changes files to archive directory" execute_hook post_debian_packages "$debuild_dir" "$deb_archive_dir" } _gendoc_md_html() { local md="$1" local docname="$(basename "$md" .md)" local dir="$(dirname "$md")" local html="$dir/$docname.html" local tmpfile="$(maketemp .md)" echo "Generating $docname.md -> $docname.html ..." sed -e 's|\.md)|.html)|g' "$md" > "$tmpfile" ||\ die "Failed to update links during markdown -> html" pandoc -s -M "title=$docname" -o "$html" "$tmpfile" ||\ die "Failed to convert markdown -> html" } _gendoc_rst_md() { local rst="$1" local docname="$(basename "$rst" .rst)" local dir="$(dirname "$rst")" local md="$dir/$docname.md" local tmpfile="$(maketemp .rst)" echo "Generating $docname.rst -> $docname.md ..." sed -e 's|\.rst>`_|.md>`_|g' "$rst" > "$tmpfile" ||\ die "Failed to update links during reStructuredText -> markdown" pandoc -s -M "title=$docname" -o "$md" "$tmpfile" ||\ die "Failed to convert reStructuredText -> markdown" } _gendoc_rst_html() { local rst="$1" local docname="$(basename "$rst" .rst)" local dir="$(dirname "$rst")" local html="$dir/$docname.html" local tmpfile="$(maketemp .rst)" echo "Generating $docname.rst -> $docname.html ..." sed -e 's|\.rst>`_|.html>`_|g' "$rst" > "$tmpfile" ||\ die "Failed to update links during reStructuredText -> html" pandoc -s -M "title=$docname" -o "$html" "$tmpfile" ||\ die "Failed to convert reStructuredText -> html" } make_documentation() { [ $opt_nodoc -eq 0 ] || return info "Creating documentation" assert_program pandoc "pandoc converter" execute_hook pre_documentation "$checkout_dir" local old_IFS="$IFS" IFS=' ' # Build markdown -> html for file in $(find "$checkout_dir" -name '*.md'); do _gendoc_md_html "$file" done # Build reStructuredText -> markdown for file in $(find "$checkout_dir" -name '*.rst'); do _gendoc_rst_md "$file" done # Build reStructuredText -> html for file in $(find "$checkout_dir" -name '*.rst'); do _gendoc_rst_html "$file" done IFS="$old_IFS" execute_hook post_documentation "$checkout_dir" } make_archives() { archive_dir="$tmpdir/$project-archives" mkdir -p "$archive_dir" || die "Failed to create archive directory" setup_py_targets= info "Creating archives" execute_hook pre_archives "$archive_dir" "$checkout_dir" for artype in $opt_archives; do local archive= local compressor= case "$artype" in tar) archive="$release_name.tar" ;; tar.bz2) compressor="bzip2 -9" archive="$release_name.tar.bz2" ;; tar.gz) compressor="gzip -9" archive="$release_name.tar.gz" ;; tar.xz) compressor="xz -9" archive="$release_name.tar.xz" ;; tar.zst) compressor="zstd -T0 -19" archive="$release_name.tar.zst" ;; zip) archive="$release_name.zip" ;; 7z) archive="$release_name.7z" ;; py-*) local target="$(echo "$artype" | sed -e 's/py-//' | tr '-' '_')" setup_py_targets="$setup_py_targets $target" continue # Python archives are handled later ;; *) die "Internal error: archive type: $artype" ;; esac info "Creating $archive" cd "$tmpdir" || die "Internal error: cd" case "$artype" in zip) zip -9 -r "$archive" "$release_name" || \ die "Failed to create ZIP archive" ;; 7z) "$SEVENZIP" -mx=9 a "$archive" "$release_name" || \ die "Failed to create 7-ZIP archive" ;; tar) tar \ --numeric-owner --owner=0 --group=0 \ --mtime='1970-01-01 00:00Z' \ --sort=name \ -c "$release_name" \ > "$archive" || \ die "Failed to create tarball" ;; tar.*) tar \ --numeric-owner --owner=0 --group=0 \ --mtime='1970-01-01 00:00Z' \ --sort=name \ -c "$release_name" \ | $compressor \ > "$archive" || \ die "Failed to create tarball" ;; *) die "Internal error: Archive type" ;; esac mv "$archive" "$archive_dir"/ || die "Failed to move archive" done # Handle Python build targets [ -f "./setup.py" ] && [ $opt_upload -ne 0 ] && setup_py_targets="$setup_py_targets sdist_gz" if [ -n "$setup_py_targets" ]; then cd "$checkout_dir" || die "Internal error: cd" [ -x "./setup.py" ] ||\ die "Used Python archive target, but no executable setup.py found" rm -rf ./dist/ || die "Failed to delete ./dist/" mkdir "$archive_dir/python" || die "Failed to create Python archive subdir" local have_bdist_wininst=0 local have_sdist=0 local have_sdist_bz2=0 local have_sdist_xz=0 local have_sdist_zip=0 for target in $setup_py_targets; do info "Building Python $target-package" local opts= local setup_target="$target" local doit=0 if [ "$target" = "bdist_wininst" ]; then [ $have_bdist_wininst -ne 0 ] && continue local have_bdist_wininst=1 local opts="--plat-name win32" local doit=1 elif [ "$target" = "sdist" -o "$target" = "sdist_gz" ]; then [ $have_sdist -ne 0 ] && continue local have_sdist=1 local setup_target="sdist" local opts="--formats=gztar --owner=root --group=root" local doit=1 elif [ "$target" = "sdist_bz2" ]; then [ $have_sdist_bz2 -ne 0 ] && continue local have_sdist_bz2=1 local setup_target="sdist" local opts="--formats=bztar --owner=root --group=root" local doit=1 elif [ "$target" = "sdist_xz" ]; then [ $have_sdist_xz -ne 0 ] && continue local have_sdist_xz=1 local setup_target="sdist" local opts="--formats=xztar --owner=root --group=root" local doit=1 elif [ "$target" = "sdist_zip" ]; then [ $have_sdist_zip -ne 0 ] && continue local have_sdist_zip=1 local setup_target="sdist" local opts="--formats=zip" local doit=1 fi if [ $doit -ne 0 ]; then ./setup.py --no-user-cfg "$setup_target" $opts ||\ die "Failed to build Python archive" fi done mv ./dist/* "$archive_dir/python/" || die "Failed to move Python archives" rmdir ./dist/ || die "Failed to delete ./dist/" fi execute_hook post_archives "$archive_dir" "$checkout_dir" } make_documentation_archives() { [ $opt_nodoc -eq 0 ] || return info "Packing documentation archives" execute_hook pre_doc_archives "$archive_dir" "$checkout_dir" execute_hook doc_archives "$archive_dir" "$checkout_dir" execute_hook post_doc_archives "$archive_dir" "$checkout_dir" } make_upload() { [ $opt_upload -eq 0 ] && return if [ -f "$checkout_dir/setup.py" ]; then # This is is a Python package. [ -n "$setup_py_targets" ] ||\ die "PyPi-upload requested, but no Python packages have been built." info "Uploading $setup_py_targets archives to PyPi" assert_program twine local py_archive_dir="$archive_dir/python" execute_hook pre_upload "$checkout_dir" "$py_archive_dir" twine check "$py_archive_dir"/*.tar.gz* ||\ die "twine check failed" dry_run twine upload --repository pypi "$py_archive_dir"/*.tar.gz* ||\ die "twine upload failed" execute_hook post_upload "$checkout_dir" "$py_archive_dir" elif [ -f "$checkout_dir/Cargo.toml" ]; then # This is a rust/cargo package. info "Uploading archive to crates.io" assert_program cargo cd "$checkout_dir" || die "Internal error: cd" execute_hook pre_upload "$checkout_dir" local dry= is_dry_run && dry="--dry-run" do_cargo publish $dry || die "cargo publish failed." execute_hook post_upload "$checkout_dir" else die "upload: Unknown package format." fi } make_testbuild() { [ $opt_nobuild -eq 0 ] || return info "Running test build" execute_hook testbuild "$checkout_dir" } make_regression_tests() { [ $opt_nobuild -eq 0 ] || return [ $opt_notests -eq 0 ] || return info "Running regression tests" execute_hook regression_tests "$checkout_dir" } # $1=directory gpg_sign_dir_recursive() { local dir="$1" # Don't sign files in the debian directory [ "$(basename "$dir")" = "debian" ] && return local path= for path in "$dir"/*; do [ -d "$path" ] && { gpg_sign_dir_recursive "$path" continue } [ -r "$path" ] || die "Sign: File not readable: $path" local filename="$(basename "$path")" local signature="$filename.asc" info "Creating signature $signature" cd "$dir" || die "Internal error: cd" local gpg=gpg have_program gpg2 && gpg=gpg2 assert_program $gpg "GNU Privacy Guard" local gpg_opts= [ -n "$GPG_KEY_RELEASE" ] && gpg_opts="--default-key $GPG_KEY_RELEASE" $gpg $gpg_opts -ab "./$filename" || die "Failed to sign $filename" done } make_archive_signatures() { [ $opt_nosign -ne 0 ] && return execute_hook pre_archive_signatures "$archive_dir" gpg_sign_dir_recursive "$archive_dir" execute_hook post_archive_signatures "$archive_dir" } make_tag() { [ $conf_notag -ne 0 ] && return [ $opt_notag -ne 0 ] && return [ "$repos_type" = "none" ] && return info "Tagging repository" tag_name="$project-$version" tag_message="$project-$version release" execute_hook pre_tag "$srcdir" cd "$srcdir" || die "Internal error: cd" case "$repos_type" in git) assert_program git local opts= [ $opt_nosign -eq 0 ] && { [ -n "$GPG_KEY_RELEASE" ] && { opts="$opts -u $GPG_KEY_RELEASE" } || { opts="$opts -s" # default key } } || { opts="$opts -a" # unsigned } local branch="$(git_main_branch "$srcdir/.git")" [ -n "$opt_ref" ] && branch="$opt_ref" export GIT_DIR="$srcdir/.git" dry_run git tag $opts -m "$tag_message" "$tag_name" "$branch" ;; *) die "tagging: Unknown repos_type" ;; esac execute_hook post_tag "$srcdir" } move_files() { local target_dir="$srcdir/$srcsubdir/release-archives" info "Moving files" execute_hook pre_move_files "$archive_dir" "$srcdir/$srcsubdir" dry_run mkdir -p "$target_dir" || die "Failed to create target directory" dry_run cp -r "$archive_dir"/* "$target_dir"/ || \ die "Failed to copy tarballs" execute_hook post_move_files "$archive_dir" "$srcdir/$srcsubdir" local dry= is_dry_run && dry=" (DRY RUN)" echo info "Built $project release ${version}${dry}" } help() { echo "Usage: $0 [OPTIONS]" echo echo "Environment:" echo " MAKERELEASE_LIB May be set to makerelease.lib" echo echo "Options:" echo " -y|--dry-run Do not make persistent changes" echo " -t|--no-tag Do not create the repository tag" echo " -s|--no-sign Do not sign" echo " -b|--no-build Do not run build. (Implies -T)" echo " -T|--no-tests Do not run regression tests" echo " -q|--quick Quick run. Equivalent to -t -s -b -T -d" echo " -D|--no-doc Do not build documentation" echo " -r|--ref REF Checkout version control reference REF" echo " -a|--archives TYPE,TYPE,... Archive type list. Default: $default_archives" echo " Possible types: tar, tar.bz2, tar.gz, tar.xz, tar.zst, zip, 7z" echo " For Python programs: py-sdist," echo " py-sdist-gz, py-sdist-bz2, py-sdist-xz, py-dist-zip," echo " py-bdist, py-bdist-wininst," echo " py-bdist-dumb, py-bdist-rpm" echo " -d|--no-debian Do not build Debian packages." echo " -U|--upload Upload the archives to PyPi or crates.io" echo " -O|--upload-only Run the bare minimum to upload only." echo " Equivalent to: -U -t -b -T -d" echo " -V|--extraversion XX Append XX to version string" echo " -K|--keep-tmp Do not delete temporary files" echo " -h|--help Show this help text" } # This is the main function called from the main script. # Parameters to this functions must be the main script arguments. makerelease() { # Backwards compatibility for old default_compress option [ -n "$default_compress" -a -z "$default_archives" ] && default_archives="$default_compress" [ -n "$project" ] || die "\$project variable not set" [ -n "$srcdir" ] || die "\$srcdir variable not set" [ -n "$srcsubdir" ] || srcsubdir="" [ -n "$conf_package" ] || conf_package="" [ -n "$tmp_basedir" ] || tmp_basedir="/tmp" [ -n "$default_archives" ] || default_archives="tar.xz" [ -n "$conf_testbuild" ] || conf_testbuild="" [ -n "$conf_notag" ] || conf_notag=0 trap terminating_signal TERM INT trap cleanup EXIT # Reproducible builds. export PYTHONHASHSEED=1 export SOURCE_DATE_EPOCH=0 local template="makerelease-$project.XXXXXXXX" tmpdir="$(mktemp -d --tmpdir="$tmp_basedir" "$template")" [ -d "$tmpdir" ] || die "Failed to create temporary directory" if have_program 7z; then SEVENZIP=7z elif have_program 7zz; then SEVENZIP=7zz else die "Program 7-Zip not found" fi opt_dryrun=0 opt_notag=0 opt_nosign=0 opt_nobuild=0 opt_nodebian=0 opt_notests=0 opt_nodoc=0 opt_ref= opt_archives="$default_archives" opt_upload=0 opt_extraversion= opt_keeptmp=0 while [ $# -ge 1 ]; do case "$1" in --help|-h) help "$@" abort 0 ;; -y|--dry-run) opt_dryrun=1 ;; -t|--no-tag) opt_notag=1 ;; -s|--no-sign) opt_nosign=1 ;; -b|--no-build) opt_nobuild=1 ;; -T|--no-tests) opt_notests=1 ;; -q|--quick) opt_notag=1 opt_nosign=1 opt_nobuild=1 opt_nodebian=1 opt_notests=1 ;; -D|--no-doc) opt_nodoc=1 ;; -r|--ref) shift opt_ref=$1 [ -n "$opt_ref" ] || die "Invalid --ref" ;; -a|--archives) shift opt_archives="$1" ;; -d|--no-debian) opt_nodebian=1 ;; -U|--upload) opt_upload=1 ;; -O|--upload-only) opt_notag=1 opt_nobuild=1 opt_nodebian=1 opt_notests=1 opt_upload=1 ;; -V|--extraversion) shift opt_extraversion="$1" [ -n "$opt_extraversion" ] || die "Invalid --extraversion" ;; -K|--keep-tmp) opt_keeptmp=1 ;; *) die "Invalid option: $1" ;; esac shift done opt_archives="$(echo "$opt_archives" | tr ',' ' ')" for artype in $opt_archives; do case "$artype" in tar|tar.bz2|tar.gz|tar.xz|tar.zst|zip|7z|py-sdist|py-sdist-gz|py-sdist-bz2|py-sdist-xz|py-sdist-zip|py-bdist|py-bdist-wininst|py-bdist-dump|py-bdist-rpm) ;; # ok *) die "Invalid archiving method: $artype" ;; esac done [ -n "$(echo "$opt_archives" | tr -d '[:blank:]')" ] ||\ die "No archiving method selected" detect_repos_type make_checkout detect_versioning relocate_checkout make_documentation make_archives make_documentation_archives make_testbuild make_regression_tests make_debian_packages make_archive_signatures make_tag make_upload move_files cleanup } # vim: syntax=sh