• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/bash
2# Copyright 2022 Google LLC
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15################################################################################
16
17# This script performs a source release on GitHub for a given repo, that is:
18# - Creates a release branch (if it does not yet exist),
19# - Creates a release tag.
20set -eo pipefail
21
22# Parameters and arguments. These will be populated/modified by args parsing.
23
24# Options.
25# Whether to actually create a release. This is false by default and meant to
26# prevent accidental releases.
27DO_RUN_ACTION="false"
28# Commit at which to make the release. If unspecified, the release is made from
29# HEAD.
30COMMIT_HASH=
31# Optional personal access token.
32ACCESS_TOKEN=
33
34# Arguments.
35# Action to be performed.
36ACTION=
37# This must be of the form `MAJOR.MINOR.PATCH`.
38VERSION=
39# Repo name after github.com/tink-crypto/, e.g., tink-cc.
40REPO_NAME=
41
42# Derived variables.
43GITHUB_REPO_URL=
44RELEASE_BRANCH=
45TAG=
46GITHUB_REFS=
47BRANCH_EXISTS="false"
48
49# Constants.
50readonly GITHUB_ORG_URL="github.com/tink-crypto"
51
52usage() {
53  echo "Usage: $0 [-rh] [-c <commit hash>] [-t <access token>] <action> \\"
54  echo "         <version> <repository>"
55  echo " <action>: The action to be performed (crete_branch|create_tag)."
56  echo " <version>: The version identifier in MAJOR.MINOR.PATCH format."
57  echo " <repository>: The name of the repository (e.g. \"tink-cc\")."
58  echo " -c: Commit hash to use as HEAD of the release branch (optional)."
59  echo " -t: Access token. Without this, the default is SSH (optional)."
60  echo " -r: Whether to actually create a release; this is false by default."
61  echo " -h: Show this help message."
62  exit 1
63}
64
65process_params() {
66  while getopts "rhc:t:" opt; do
67    case "${opt}" in
68      r) DO_RUN_ACTION="true" ;;
69      c) COMMIT_HASH="${OPTARG}" ;;
70      t) ACCESS_TOKEN="${OPTARG}" ;;
71      *) usage ;;
72    esac
73  done
74  shift $((OPTIND - 1))
75  readonly DO_RUN_ACTION
76  readonly COMMIT_HASH
77  readonly ACCESS_TOKEN
78
79  ACTION="$1"
80  if [[ ! "${ACTION}" =~ create_branch|create_tag ]]; then
81    echo "ERROR: Expected (create_branch|create_tag) got ${ACTION}" >&2
82    usage
83  fi
84  readonly ACTION
85
86  VERSION="$2"
87  if [[ ! "${VERSION}" =~ ^[0-9]+.[0-9]+.[0-9]+$ ]]; then
88    echo "ERROR: Invalid version format: expected MAJOR.MINOR.PATCH, got \
89${VERSION}" >&2
90    usage
91  fi
92  readonly VERSION
93
94  REPO_NAME="$3"
95  if [[ -z "${REPO_NAME}" ]]; then
96    echo "ERROR: Repo name must be specified." >&2
97    usage
98  fi
99  readonly REPO_NAME
100
101  # Use SSH by default.
102  local protocol_and_credentials="ssh://git"
103  if [[ -n "${ACCESS_TOKEN}" ]]; then
104    protocol_and_credentials="https://ise-crypto:${ACCESS_TOKEN}"
105  fi
106  readonly protocol_and_credentials
107  GITHUB_REPO_URL="${protocol_and_credentials}@${GITHUB_ORG_URL}/${REPO_NAME}"
108  readonly GITHUB_REPO_URL
109
110  # Release branch is only MAJOR.MINOR.
111  readonly RELEASE_BRANCH="$(echo "${VERSION}" | cut -d'.' -f1,2)"
112
113  # Splitting declaration and assignment guarantees correct propagation of the
114  # exit code of the subshell.
115  local GITHUB_REFS
116  GITHUB_REFS="$(git ls-remote "${GITHUB_REPO_URL}")"
117  readonly GITHUB_REFS
118
119  local -r expected_release_branch="refs/heads/${RELEASE_BRANCH}"
120  if echo "${GITHUB_REFS}" | grep "${expected_release_branch}" > /dev/null; then
121    BRANCH_EXISTS="true"
122  fi
123  readonly BRANCH_EXISTS
124
125  if [[ "${ACTION}" == "create_tag" ]]; then
126    if [[ "${BRANCH_EXISTS}" == "false" ]]; then
127      echo "ERROR: The release branch does not exist in \
128${GITHUB_ORG_URL}/${REPO_NAME}." >&2
129      return 1
130    fi
131    local -r release_tag="v${VERSION}"
132    local -r expected_release_tag="refs/tags/${release_tag}"
133    if echo "${GITHUB_REFS}" | grep "${expected_release_tag}" > /dev/null; then
134      echo "ERROR The tag \"${release_tag}\" already exists in \
135${GITHUB_ORG_URL}/${REPO_NAME}." >&2
136      return 1
137    fi
138
139  fi
140}
141
142#######################################
143# Prints a command
144#
145# Args:
146#   Command to execute.
147#
148#######################################
149print_command() {
150  printf '%q ' '+' "$@"
151  echo
152}
153
154#######################################
155# Runs a command if DO_RUN_ACTION is true.
156#
157# Args:
158#   Command to execute.
159# Globals:
160#   DO_RUN_ACTION
161#
162#######################################
163run_command() {
164  if [[ "${DO_RUN_ACTION}" == "false" ]]; then
165    echo "  *** Dry run, command not executed. ***"
166    return 0
167  fi
168  # Actually run the command.
169  "$@"
170  return $?
171}
172
173#######################################
174# Prints and runs a command.
175#
176# Args:
177#   Command to execute.
178#
179#######################################
180print_and_run_command() {
181  print_command "$@"
182  run_command "$@"
183}
184
185#######################################
186# Creates and checks out to the release branch.
187#
188# If COMMIT_HASH is specified, use COMMIT_HASH as HEAD for the branch.
189#
190# Globals:
191#   RELEASE_BRANCH
192#   COMMIT_HASH
193#
194#######################################
195git_create_release_branch() {
196  if [[ "${BRANCH_EXISTS}" == "true" ]]; then
197    echo "WARNING: The release branch already exists. Nothing to do."
198    return 0
199  fi
200  # Target branch does not exist so we create the release branch.
201  if [[ -n "${COMMIT_HASH:-}" ]]; then
202    # Use COMMIT_HASH as HEAD for this branch.
203    print_and_run_command git branch "${RELEASE_BRANCH}" "${COMMIT_HASH}"
204  else
205    print_and_run_command git branch "${RELEASE_BRANCH}"
206  fi
207  print_and_run_command git push origin "${RELEASE_BRANCH}"
208}
209
210#######################################
211# Creates a release tag.
212#
213# Globals:
214#   RELEASE_BRANCH
215#   REPO_NAME
216#   VERSION
217#
218#######################################
219git_create_release_tag() {
220  if [[ "${BRANCH_EXISTS}" == "false" ]]; then
221    echo "ERROR: The release branch does not exist in \
222${GITHUB_ORG_URL}/${REPO_NAME}." >&2
223    return 1
224  fi
225  local -r release_tag="v${VERSION}"
226  local -r expected_release_tag="refs/tags/${release_tag}"
227  if echo "${GITHUB_REFS}" | grep "${expected_release_tag}" > /dev/null; then
228    echo "ERROR The tag \"${release_tag}\" already exists in \
229${GITHUB_ORG_URL}/${REPO_NAME}." >&2
230    return 1
231  fi
232  print_and_run_command git checkout "${RELEASE_BRANCH}"
233  print_and_run_command git tag -a "${release_tag}" \
234    -m "${REPO_NAME} version ${VERSION}"
235  print_and_run_command git push origin "${release_tag}"
236}
237
238main() {
239  process_params "$@"
240  # Avoid logging the full URL; replace GIT_URL with a version that omits user
241  # and access token.
242  local -r protocol="$(echo "${GITHUB_REPO_URL}" | cut -d':' -f1)"
243  local -r github_repo="$(echo "${GITHUB_REPO_URL}" | cut -d'@' -f2)"
244  print_command git clone "${protocol}://...@${github_repo}"
245  run_command git clone "${GITHUB_REPO_URL}"
246  print_and_run_command cd "${REPO_NAME}"
247
248  case "${ACTION}" in
249    create_branch) git_create_release_branch ;;
250    create_tag) git_create_release_tag ;;
251  esac
252}
253
254main "$@"
255