diff --git a/Setlist/Properties/AssemblyInfo.cs b/Setlist/Properties/AssemblyInfo.cs index 51454d5..1822718 100644 --- a/Setlist/Properties/AssemblyInfo.cs +++ b/Setlist/Properties/AssemblyInfo.cs @@ -11,5 +11,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("50F53E6E-21D5-4780-8E67-273877DAA28C")] -[assembly: AssemblyVersion("0.1.1.0")] -[assembly: AssemblyFileVersion("0.1.1.0")] +[assembly: AssemblyVersion("0.1.2.0")] +[assembly: AssemblyFileVersion("0.1.2.0")] diff --git a/Setlist/manifest.json b/Setlist/manifest.json index 8b2c090..2bde94c 100644 --- a/Setlist/manifest.json +++ b/Setlist/manifest.json @@ -3,7 +3,7 @@ "id": "Setlist", "name": "Setlist", "author": "", - "version": "0.1.1", + "version": "0.1.2", "description": "Syncs playlists with external sources.", "gameVersion": "1.44.1", "dependsOn": { diff --git a/scripts/release-gitea.sh b/scripts/release-gitea.sh new file mode 100755 index 0000000..d307cea --- /dev/null +++ b/scripts/release-gitea.sh @@ -0,0 +1,203 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(git rev-parse --show-toplevel)" +cd "$repo_root" + +if [[ -f .env ]]; then + set -a + # shellcheck source=/dev/null + . ./.env + set +a +fi + +project="Setlist/Setlist.csproj" +manifest="Setlist/manifest.json" +assembly_info="Setlist/Properties/AssemblyInfo.cs" +version="${1:-$(jq -r '.version' "$manifest")}" +tag="v${version}" +game_version="$(jq -r '.gameVersion' "$manifest")" + +require() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "error: required command not found: $1" >&2 + exit 1 + fi +} + +require curl +require dotnet +require git +require jq +require rg +require zip + +if [[ -z "${GITEA_AGENT_TOKEN:-}" ]]; then + echo "error: GITEA_AGENT_TOKEN is not set; add it to .env or export it" >&2 + exit 1 +fi + +manifest_version="$(jq -r '.version' "$manifest")" +if [[ "$version" != "$manifest_version" ]]; then + echo "error: requested version $version does not match manifest version $manifest_version" >&2 + exit 1 +fi + +if ! rg -q "\\[assembly: AssemblyVersion\\(\"${version}\\.0\"\\)\\]" "$assembly_info"; then + echo "error: AssemblyVersion does not match ${version}.0" >&2 + exit 1 +fi + +if ! rg -q "\\[assembly: AssemblyFileVersion\\(\"${version}\\.0\"\\)\\]" "$assembly_info"; then + echo "error: AssemblyFileVersion does not match ${version}.0" >&2 + exit 1 +fi + +if ! git diff --quiet --exit-code -- . ':(exclude)Setlist/bin' ':(exclude)Setlist/obj'; then + echo "error: tracked worktree changes are present; commit them before releasing" >&2 + exit 1 +fi + +branch="$(git symbolic-ref --quiet --short HEAD)" +if [[ -z "$branch" ]]; then + echo "error: releases must be run from a branch, not detached HEAD" >&2 + exit 1 +fi + +origin_url="$(git remote get-url origin)" +case "$origin_url" in + git@*:*|gitea@*:*) + host_part="${origin_url%%:*}" + host="${host_part#*@}" + repo_path="${origin_url#*:}" + ;; + ssh://*@*/*) + without_scheme="${origin_url#ssh://}" + without_user="${without_scheme#*@}" + host="${without_user%%/*}" + repo_path="${without_user#*/}" + ;; + http://*/*|https://*/*) + without_scheme="${origin_url#*://}" + host="${without_scheme%%/*}" + repo_path="${without_scheme#*/}" + ;; + *) + echo "error: cannot parse origin URL: $origin_url" >&2 + exit 1 + ;; +esac +repo_path="${repo_path%.git}" +api_base="${GITEA_API_BASE:-https://${host}/api/v1}" + +echo "Restoring $project" +dotnet restore "$project" + +echo "Building $project Release" +dotnet build "$project" -c Release + +short_sha="$(git rev-parse --short=7 HEAD)" +asset_name="Setlist-${version}-bs${game_version}-${short_sha}.zip" +asset_dir="Setlist/bin/Release/zip" +asset_path="${asset_dir}/${asset_name}" +package_root="$(mktemp -d)" +response_body="$(mktemp)" +request_body="$(mktemp)" +curl_config="$(mktemp)" + +cleanup() { + rm -rf "$package_root" + rm -f "$response_body" "$request_body" "$curl_config" +} +trap cleanup EXIT + +chmod 600 "$curl_config" +{ + printf 'silent\n' + printf 'show-error\n' + printf 'header = "Authorization: token %s"\n' "$GITEA_AGENT_TOKEN" +} > "$curl_config" + +mkdir -p "$asset_dir" "$package_root/Plugins" +cp Setlist/bin/Release/Setlist.dll "$package_root/Plugins/Setlist.dll" +cp Setlist/bin/Release/Setlist.pdb "$package_root/Plugins/Setlist.pdb" +rm -f "$asset_path" +(cd "$package_root" && zip -qr "$repo_root/$asset_path" Plugins) + +echo "Packaged $asset_path" +sha256sum "$asset_path" + +if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then + tag_target="$(git rev-list -n 1 "$tag")" + head_target="$(git rev-parse HEAD)" + if [[ "$tag_target" != "$head_target" ]]; then + echo "error: local tag $tag points at $tag_target, not HEAD $head_target" >&2 + exit 1 + fi +else + git tag -a "$tag" -m "Setlist $tag" +fi + +echo "Pushing $branch and $tag to origin" +git push origin "HEAD:${branch}" +git push origin "$tag" + +release_url="${api_base}/repos/${repo_path}/releases/tags/${tag}" +status="$(curl --config "$curl_config" -o "$response_body" -w '%{http_code}' "$release_url" || true)" + +if [[ "$status" == "200" ]]; then + release_id="$(jq -r '.id' "$response_body")" + echo "Using existing Gitea release $tag" +elif [[ "$status" == "404" ]]; then + jq -n \ + --arg tag "$tag" \ + --arg name "$tag" \ + --arg body "Release ${tag} for Beat Saber ${game_version}." \ + --arg target "$branch" \ + '{tag_name:$tag, target_commitish:$target, name:$name, body:$body, draft:false, prerelease:false}' \ + > "$request_body" + status="$(curl --config "$curl_config" -X POST -H 'Content-Type: application/json' --data @"$request_body" -o "$response_body" -w '%{http_code}' "${api_base}/repos/${repo_path}/releases" || true)" + if [[ "$status" != "201" ]]; then + echo "error: failed to create release; Gitea returned HTTP $status" >&2 + cat "$response_body" >&2 + exit 1 + fi + release_id="$(jq -r '.id' "$response_body")" + echo "Created Gitea release $tag" +else + echo "error: failed to inspect release; Gitea returned HTTP $status" >&2 + cat "$response_body" >&2 + exit 1 +fi + +assets_url="${api_base}/repos/${repo_path}/releases/${release_id}/assets" +status="$(curl --config "$curl_config" -o "$response_body" -w '%{http_code}' "$assets_url" || true)" +if [[ "$status" != "200" ]]; then + echo "error: failed to list release assets; Gitea returned HTTP $status" >&2 + cat "$response_body" >&2 + exit 1 +fi + +existing_asset_id="$(jq -r --arg name "$asset_name" '.[] | select(.name == $name) | .id' "$response_body" | head -n 1)" +if [[ -n "$existing_asset_id" ]]; then + echo "Deleting existing asset $asset_name" + status="$(curl --config "$curl_config" -X DELETE -o "$response_body" -w '%{http_code}' "${assets_url}/${existing_asset_id}" || true)" + if [[ "$status" != "204" ]]; then + echo "error: failed to delete existing asset; Gitea returned HTTP $status" >&2 + cat "$response_body" >&2 + exit 1 + fi +fi + +status="$(curl --config "$curl_config" -X POST -H 'Content-Type: application/zip' --data-binary @"$asset_path" -o "$response_body" -w '%{http_code}' "${assets_url}?name=${asset_name}" || true)" +if [[ "$status" != "201" ]]; then + echo "error: failed to upload release asset; Gitea returned HTTP $status" >&2 + cat "$response_body" >&2 + exit 1 +fi + +html_url="$(jq -r '.browser_download_url // .download_url // empty' "$response_body")" +echo "Uploaded $asset_name" +if [[ -n "$html_url" ]]; then + echo "$html_url" +fi