/ package / recreateRelease.sh
recreateRelease.sh
  1  #!/usr/bin/env bash
  2  set -euo pipefail
  3  
  4  usage() {
  5    cat <<'USAGE'
  6  Usage:
  7    recreateLatestRelease.sh \
  8      --gitlab-host https://gitlab.example.com \
  9      --project-id 123 \
 10      --token <YOUR_TOKEN> \
 11      --tag-ref master \
 12      [--release-tag latest] \
 13      [--release-name latest] \
 14      [--release-description "Automated latest release"]
 15  
 16  What it does:
 17    1) Deletes the release for --release-tag (if it exists)
 18    2) Deletes the tag named --release-tag (if it exists)
 19    3) Creates a new tag --release-tag from --tag-ref
 20    4) Creates a new release attached to that tag
 21  
 22  Notes:
 23    - A GitLab release is always attached to a git tag.
 24    - --project-id can be numeric ID or URL-encoded path (e.g. group%2Fproject).
 25    - Token should have permissions to manage releases + tags (typically PAT with api scope).
 26  USAGE
 27  }
 28  
 29  need() { command -v "$1" >/dev/null 2>&1 || { echo "Missing dependency: $1" >&2; exit 1; }; }
 30  
 31  # --- Parse args ---
 32  GITLAB_HOST=""
 33  PROJECT_ID=""
 34  TOKEN=""
 35  TAG_REF=""
 36  RELEASE_TAG="latest"
 37  RELEASE_NAME="latest"
 38  RELEASE_DESCRIPTION="Automated latest release created by CI."
 39  
 40  while [[ $# -gt 0 ]]; do
 41    case "$1" in
 42      --gitlab-host)          GITLAB_HOST="${2:-}"; shift 2 ;;
 43      --project-id)           PROJECT_ID="${2:-}"; shift 2 ;;
 44      --token)                TOKEN="${2:-}"; shift 2 ;;
 45      --tag-ref)              TAG_REF="${2:-}"; shift 2 ;;
 46      --release-tag)          RELEASE_TAG="${2:-}"; shift 2 ;;
 47      --release-name)         RELEASE_NAME="${2:-}"; shift 2 ;;
 48      --release-description)  RELEASE_DESCRIPTION="${2:-}"; shift 2 ;;
 49      -h|--help)              usage; exit 0 ;;
 50      *) echo "Unknown argument: $1" >&2; usage; exit 2 ;;
 51    esac
 52  done
 53  
 54  # --- Validate ---
 55  [[ -n "$GITLAB_HOST" ]] || { echo "Missing --gitlab-host" >&2; usage; exit 2; }
 56  [[ -n "$PROJECT_ID"  ]] || { echo "Missing --project-id" >&2; usage; exit 2; }
 57  [[ -n "$TOKEN"       ]] || { echo "Missing --token" >&2; usage; exit 2; }
 58  [[ -n "$TAG_REF"     ]] || { echo "Missing --tag-ref" >&2; usage; exit 2; }
 59  
 60  need curl
 61  need jq
 62  
 63  api_base="${GITLAB_HOST%/}/api/v4"
 64  encoded_tag="$(jq -rn --arg s "$RELEASE_TAG" '$s|@uri')"
 65  
 66  echo "GitLab host:        ${GITLAB_HOST%/}"
 67  echo "Project ID:         ${PROJECT_ID}"
 68  echo "Release/tag name:   ${RELEASE_TAG}"
 69  echo "Tag ref:            ${TAG_REF}"
 70  echo "Release name:       ${RELEASE_NAME}"
 71  echo "Release desc:       ${RELEASE_DESCRIPTION}"
 72  echo
 73  
 74  # --- Delete existing release (ignore if missing) ---
 75  echo "Deleting existing release '${RELEASE_TAG}' (if it exists)..."
 76  release_get_code="$(
 77    curl --insecure -sS -o /dev/null -w "%{http_code}" \
 78      --header "PRIVATE-TOKEN: ${TOKEN}" \
 79      "${api_base}/projects/${PROJECT_ID}/releases/${encoded_tag}"
 80  )"
 81  
 82  if [[ "$release_get_code" == "200" ]]; then
 83    curl --insecure -fsS -X DELETE \
 84      --header "PRIVATE-TOKEN: ${TOKEN}" \
 85      "${api_base}/projects/${PROJECT_ID}/releases/${encoded_tag}" \
 86      >/dev/null
 87    echo "Release deleted."
 88  elif [[ "$release_get_code" == "404" ]]; then
 89    echo "Release not found; nothing to delete."
 90  else
 91    echo "ERROR: Unable to check release '${RELEASE_TAG}'. HTTP status: ${release_get_code}" >&2
 92    exit 1
 93  fi
 94  
 95  echo
 96  
 97  # --- Delete existing tag (ignore if missing) ---
 98  echo "Deleting existing tag '${RELEASE_TAG}' (if it exists)..."
 99  tag_get_code="$(
100    curl --insecure -sS -o /dev/null -w "%{http_code}" \
101      --header "PRIVATE-TOKEN: ${TOKEN}" \
102      "${api_base}/projects/${PROJECT_ID}/repository/tags/${encoded_tag}"
103  )"
104  
105  if [[ "$tag_get_code" == "200" ]]; then
106    curl --insecure -fsS -X DELETE \
107      --header "PRIVATE-TOKEN: ${TOKEN}" \
108      "${api_base}/projects/${PROJECT_ID}/repository/tags/${encoded_tag}" \
109      >/dev/null
110    echo "Tag deleted."
111  elif [[ "$tag_get_code" == "404" ]]; then
112    echo "Tag not found; nothing to delete."
113  else
114    echo "ERROR: Unable to check tag '${RELEASE_TAG}'. HTTP status: ${tag_get_code}" >&2
115    exit 1
116  fi
117  
118  echo
119  
120  # --- Create tag ---
121  echo "Creating tag '${RELEASE_TAG}' from ref '${TAG_REF}'..."
122  create_tag_code="$(
123    curl --insecure -sS -o /dev/null -w "%{http_code}" -X POST \
124      --header "PRIVATE-TOKEN: ${TOKEN}" \
125      --data-urlencode "tag_name=${RELEASE_TAG}" \
126      --data-urlencode "ref=${TAG_REF}" \
127      "${api_base}/projects/${PROJECT_ID}/repository/tags"
128  )"
129  
130  if [[ "$create_tag_code" != "201" ]]; then
131    echo "ERROR: Failed to create tag '${RELEASE_TAG}'. HTTP status: ${create_tag_code}" >&2
132    exit 1
133  fi
134  echo "Tag created."
135  echo
136  
137  # --- Create release ---
138  echo "Creating release '${RELEASE_TAG}'..."
139  create_release_code="$(
140    curl --insecure -sS -o /dev/null -w "%{http_code}" -X POST \
141      --header "PRIVATE-TOKEN: ${TOKEN}" \
142      --data-urlencode "name=${RELEASE_NAME}" \
143      --data-urlencode "tag_name=${RELEASE_TAG}" \
144      --data-urlencode "description=${RELEASE_DESCRIPTION}" \
145      "${api_base}/projects/${PROJECT_ID}/releases"
146  )"
147  
148  if [[ "$create_release_code" != "201" ]]; then
149    echo "ERROR: Failed to create release '${RELEASE_TAG}'. HTTP status: ${create_release_code}" >&2
150    exit 1
151  fi
152  
153  echo "Release created."