259 lines
5.9 KiB
Bash
259 lines
5.9 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
cd "${PROJECT_DIR}"
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage: scripts/release.sh [options]
|
|
|
|
Bundles the current lockfile into a portal archive and publishes the release to Gitea.
|
|
|
|
Options:
|
|
--lock PATH Lock file to use (default: release/toolbox.lock.json)
|
|
--tag TAG Release tag to create/push (default: v<releaseVersion>)
|
|
--owner OWNER Gitea owner/org (default: lotobo)
|
|
--repo REPO Gitea repository (default: toolbox-portal)
|
|
--api-base URL Gitea API base URL (default: https://git.add-ideas.de/api/v1)
|
|
--remote REMOTE Git remote used to push the tag (default: origin)
|
|
--build-dir DIR Build directory (default: build)
|
|
--output-subdir DIR Assembled output directory under build-dir (default: toolbox)
|
|
--skip-tests Skip npm test
|
|
--skip-assemble Skip npm run assemble
|
|
--skip-build Skip npm run build
|
|
--skip-tag Skip tagging/push of the release tag
|
|
--skip-git-push Skip pushing git branch and tag
|
|
--skip-publish Skip Gitea release publish/upload
|
|
--force Force overwrite of build outputs
|
|
--dry-run Print commands without running remote publishing operations
|
|
--help Show this help
|
|
|
|
Environment:
|
|
GITEA_TOKEN Personal token for Gitea API calls
|
|
GITEA_API_BASE Optional override for API base URL
|
|
|
|
Examples:
|
|
scripts/release.sh
|
|
scripts/release.sh --tag v0.20.4 --skip-tests
|
|
USAGE
|
|
}
|
|
|
|
LOCK_FILE="release/toolbox.lock.json"
|
|
OWNER="lotobo"
|
|
REPO="toolbox-portal"
|
|
API_BASE="${GITEA_API_BASE:-https://git.add-ideas.de/api/v1}"
|
|
REMOTE="origin"
|
|
BUILD_DIR="build"
|
|
OUTPUT_SUBDIR="toolbox"
|
|
SKIP_TESTS=0
|
|
SKIP_ASSEMBLE=0
|
|
SKIP_BUILD=0
|
|
SKIP_TAG=0
|
|
SKIP_GIT_PUSH=0
|
|
SKIP_PUBLISH=0
|
|
FORCE=0
|
|
DRY_RUN=0
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--lock)
|
|
LOCK_FILE="$2"
|
|
shift 2
|
|
;;
|
|
--tag)
|
|
TAG_OVERRIDE="$2"
|
|
shift 2
|
|
;;
|
|
--owner)
|
|
OWNER="$2"
|
|
shift 2
|
|
;;
|
|
--repo)
|
|
REPO="$2"
|
|
shift 2
|
|
;;
|
|
--api-base)
|
|
API_BASE="$2"
|
|
shift 2
|
|
;;
|
|
--remote)
|
|
REMOTE="$2"
|
|
shift 2
|
|
;;
|
|
--build-dir)
|
|
BUILD_DIR="$2"
|
|
shift 2
|
|
;;
|
|
--output-subdir)
|
|
OUTPUT_SUBDIR="$2"
|
|
shift 2
|
|
;;
|
|
--skip-tests)
|
|
SKIP_TESTS=1
|
|
shift
|
|
;;
|
|
--skip-assemble)
|
|
SKIP_ASSEMBLE=1
|
|
shift
|
|
;;
|
|
--skip-build)
|
|
SKIP_BUILD=1
|
|
shift
|
|
;;
|
|
--skip-tag)
|
|
SKIP_TAG=1
|
|
shift
|
|
;;
|
|
--skip-git-push)
|
|
SKIP_GIT_PUSH=1
|
|
shift
|
|
;;
|
|
--skip-publish)
|
|
SKIP_PUBLISH=1
|
|
shift
|
|
;;
|
|
--force)
|
|
FORCE=1
|
|
shift
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=1
|
|
shift
|
|
;;
|
|
--help|-h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown argument: $1" >&2
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
run() {
|
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
echo "DRY-RUN: $*"
|
|
return 0
|
|
fi
|
|
|
|
"$@"
|
|
}
|
|
|
|
RELEASE_VERSION="$(node -e "const fs=require('fs');const path=process.argv[1];const lock=JSON.parse(fs.readFileSync(path,'utf8'));if(!lock?.releaseVersion){process.exit(1);}console.log(lock.releaseVersion);" "$LOCK_FILE")"
|
|
if [ -z "$RELEASE_VERSION" ]; then
|
|
echo "releaseVersion could not be read from ${LOCK_FILE}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "${TAG_OVERRIDE:-}" ]; then
|
|
RELEASE_TAG="$TAG_OVERRIDE"
|
|
else
|
|
RELEASE_TAG="v${RELEASE_VERSION}"
|
|
fi
|
|
|
|
ZIP_FILE="${BUILD_DIR}/add-ideas-toolbox-${RELEASE_VERSION}.zip"
|
|
CHECKSUM_FILE="${ZIP_FILE}.sha256"
|
|
TARGET_DIR="${BUILD_DIR}/${OUTPUT_SUBDIR}"
|
|
|
|
if [ "$SKIP_TESTS" -eq 0 ]; then
|
|
run npm test
|
|
fi
|
|
|
|
if [ "$SKIP_BUILD" -eq 0 ]; then
|
|
run npm run build
|
|
fi
|
|
|
|
if [ "$SKIP_ASSEMBLE" -eq 0 ]; then
|
|
ASSEMBLE_ARGS=(
|
|
npm
|
|
run
|
|
assemble
|
|
--
|
|
--lock
|
|
"$LOCK_FILE"
|
|
--portal-dist
|
|
dist
|
|
--output
|
|
"$TARGET_DIR"
|
|
--archive
|
|
"$ZIP_FILE"
|
|
)
|
|
if [ "$FORCE" -eq 1 ]; then
|
|
ASSEMBLE_ARGS+=(--force)
|
|
fi
|
|
run "${ASSEMBLE_ARGS[@]}"
|
|
fi
|
|
|
|
if [ ! -s "$ZIP_FILE" ]; then
|
|
echo "Release archive missing: $ZIP_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -s "$CHECKSUM_FILE" ]; then
|
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
echo "DRY-RUN: would create ${CHECKSUM_FILE}"
|
|
else
|
|
sha256sum "$ZIP_FILE" > "$CHECKSUM_FILE"
|
|
fi
|
|
fi
|
|
|
|
if [ "$SKIP_TAG" -eq 0 ]; then
|
|
if git rev-parse -q --verify "refs/tags/${RELEASE_TAG}" >/dev/null; then
|
|
CURRENT_COMMIT="$(git rev-parse "${RELEASE_TAG}^{commit}")"
|
|
HEAD_COMMIT="$(git rev-parse HEAD)"
|
|
if [ "$CURRENT_COMMIT" != "$HEAD_COMMIT" ]; then
|
|
echo "Tag ${RELEASE_TAG} already exists at ${CURRENT_COMMIT}, but HEAD is ${HEAD_COMMIT}." >&2
|
|
exit 1
|
|
fi
|
|
echo "Tag exists at HEAD: ${RELEASE_TAG}"
|
|
else
|
|
echo "Creating tag ${RELEASE_TAG}"
|
|
run git tag -a "$RELEASE_TAG" -m "toolbox ${RELEASE_TAG}"
|
|
fi
|
|
|
|
if [ "$SKIP_GIT_PUSH" -eq 0 ]; then
|
|
run git push "$REMOTE" HEAD
|
|
run git push "$REMOTE" "$RELEASE_TAG"
|
|
fi
|
|
fi
|
|
|
|
if [ "$SKIP_PUBLISH" -eq 0 ]; then
|
|
TOKEN="${GITEA_TOKEN:-}"
|
|
if [ -z "$TOKEN" ]; then
|
|
if [ -f "${HOME}/.config/gitea/gitea.env" ]; then
|
|
# shellcheck disable=SC1091
|
|
. "${HOME}/.config/gitea/gitea.env"
|
|
TOKEN="${GITEA_TOKEN:-}"
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$TOKEN" ]; then
|
|
echo "GITEA_TOKEN is required for publishing (or use --skip-publish)." >&2
|
|
exit 1
|
|
fi
|
|
|
|
run node scripts/publish-release.mjs \
|
|
--api-base "$API_BASE" \
|
|
--owner "$OWNER" \
|
|
--repo "$REPO" \
|
|
--tag "$RELEASE_TAG" \
|
|
--target "$(git rev-parse HEAD)" \
|
|
--name "Toolbox ${RELEASE_TAG}" \
|
|
--archive "$ZIP_FILE" \
|
|
--checksum "$CHECKSUM_FILE" \
|
|
--lock-file "$LOCK_FILE" \
|
|
--token "$TOKEN"
|
|
|
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
echo "Dry run complete. No assets were uploaded."
|
|
else
|
|
echo "Published ${RELEASE_TAG} to Gitea."
|
|
fi
|
|
fi
|
|
|
|
echo "Release wrapper finished for ${RELEASE_TAG}"
|