Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 74 additions & 5 deletions .github/scripts/release-samples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# limitations under the License.
# ------------------------------------------------------------

set -xe
set -xeo pipefail

VERSION_NUMBER=$1 # (e.g. 0.1.0)
REPOSITORY="samples"
Expand All @@ -26,6 +26,14 @@ if [[ -z "$VERSION_NUMBER" ]]; then
exit 1
fi

# The release commit is created through the GitHub API in GITHUB_REPOSITORY (owner/name) using GH_TOKEN,
# which must be a GitHub App installation token. BOT_NAME and BOT_EMAIL identify the App's bot user for
# the DCO sign-off.
if [[ -z "${GITHUB_REPOSITORY}" || -z "${BOT_NAME}" || -z "${BOT_EMAIL}" ]]; then
echo "Error: GITHUB_REPOSITORY, BOT_NAME, and BOT_EMAIL must be set."
exit 1
fi

# CHANNEL is the major and minor version of the VERSION_NUMBER (e.g. 0.1)
CHANNEL="$(echo $VERSION_NUMBER | cut -d '.' -f 1,2)"

Expand All @@ -40,7 +48,8 @@ echo "Creating release branch for ${REPOSITORY}..."

pushd $REPOSITORY

git checkout -B "${CHANNEL_VERSION}"
# The release branch starts from the checked-out commit
BASE_SHA="$(git rev-parse HEAD)"

# Update bicepconfig.json br:biceptypes.azurecr.io/radius with the CHANNEL
BICEPCONFIG_RADIUS_STRING_REPLACEMENT="br:biceptypes.azurecr.io/radius:${CHANNEL}"
Expand All @@ -54,11 +63,71 @@ mv bicepconfig_updated.json bicepconfig.json
echo "View updated bicepconfig..."
cat bicepconfig.json

# Push changes to GitHub
echo "Running git diff..."
git diff
git add --all
git commit --signoff --message "Update samples for ${CHANNEL_VERSION}"
git push origin "${CHANNEL_VERSION}"

if git diff --cached --quiet; then
echo "Error: No changes to commit for ${CHANNEL_VERSION}."
exit 1
fi

# Create the release commit with the GitHub API instead of pushing a local commit. GitHub signs commits that a
# GitHub App creates this way when the request has no custom author, committer, or signature:
# https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification#signature-verification-for-bots
echo "Uploading changed files..."
TREE_ENTRIES=""
while IFS= read -r -d '' CHANGE && IFS= read -r -d '' CHANGED_PATH; do
read -r OLD_MODE NEW_MODE _ NEW_SHA CHANGE_TYPE <<<"${CHANGE#:}"
case "${CHANGE_TYPE}" in
A | M | T)
BLOB_SHA="$(git cat-file blob "${NEW_SHA}" | base64 --wrap=0 |
jq --raw-input --slurp '{content: ., encoding: "base64"}' |
gh api --method POST "repos/${GITHUB_REPOSITORY}/git/blobs" --input - --jq '.sha')"
TREE_ENTRIES+="$(jq --null-input --compact-output --arg path "${CHANGED_PATH}" --arg mode "${NEW_MODE}" \
--arg sha "${BLOB_SHA}" '{path: $path, mode: $mode, type: "blob", sha: $sha}')"$'\n'
;;
D)
TREE_ENTRIES+="$(jq --null-input --compact-output --arg path "${CHANGED_PATH}" --arg mode "${OLD_MODE}" \
'{path: $path, mode: $mode, type: "blob", sha: null}')"$'\n'
;;
*)
echo "Error: Unsupported change type ${CHANGE_TYPE} for ${CHANGED_PATH}."
exit 1
;;
esac
done < <(git diff-index --cached --no-renames -z HEAD)

TREE_SHA="$(jq --slurp --arg base_tree "$(git rev-parse 'HEAD^{tree}')" '{base_tree: $base_tree, tree: .}' <<<"${TREE_ENTRIES}" |
gh api --method POST "repos/${GITHUB_REPOSITORY}/git/trees" --input - --jq '.sha')"

# The commit must contain exactly the staged changes, like a local `git commit`
if [[ "${TREE_SHA}" != "$(git write-tree)" ]]; then
echo "Error: GitHub tree ${TREE_SHA} does not match the local changes."
exit 1
fi

echo "Creating commit..."
COMMIT="$(jq --null-input \
--arg message "$(printf 'Update samples for %s\n\nSigned-off-by: %s <%s>' "${CHANNEL_VERSION}" "${BOT_NAME}" "${BOT_EMAIL}")" \
--arg tree "${TREE_SHA}" \
--arg parent "${BASE_SHA}" \
'{message: $message, tree: $tree, parents: [$parent]}' |
gh api --method POST "repos/${GITHUB_REPOSITORY}/git/commits" --input -)"
COMMIT_SHA="$(jq --raw-output '.sha' <<<"${COMMIT}")"

# Nothing has been published yet, so stop here if GitHub did not sign the commit
if [[ "$(jq --raw-output '.verification.verified' <<<"${COMMIT}")" != "true" ]]; then
echo "Error: GitHub did not verify commit ${COMMIT_SHA} ($(jq --raw-output '.verification.reason' <<<"${COMMIT}")). GH_TOKEN must be a GitHub App installation token."
exit 1
fi

# Publish the commit. An existing release branch is only fast-forwarded, like `git push`.
echo "Pushing ${COMMIT_SHA} to ${CHANNEL_VERSION}..."
if gh api "repos/${GITHUB_REPOSITORY}/git/ref/heads/${CHANNEL_VERSION}" --silent 2>/dev/null; then
gh api --method PATCH "repos/${GITHUB_REPOSITORY}/git/refs/heads/${CHANNEL_VERSION}" -f sha="${COMMIT_SHA}" -F force=false --silent
else
gh api --method POST "repos/${GITHUB_REPOSITORY}/git/refs" -f ref="refs/heads/${CHANNEL_VERSION}" -f sha="${COMMIT_SHA}" --silent
fi

popd
18 changes: 8 additions & 10 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
repositories: samples

# Resolve the bot's GitHub-formatted name and email dynamically from the app slug
# so commits are attributed to the bot identity rather than hardcoded values.
# so the release commit's DCO sign-off matches the bot that authors it.
- name: Get bot details
id: bot-details
uses: raven-actions/bot-details@ee8966a9ff6e7e42cbfc4a56b4ddb60a9d1b40a6 # v1.2.0
Expand All @@ -47,15 +47,7 @@ jobs:
token: ${{ steps.app-token.outputs.token }}
ref: edge
path: samples
persist-credentials: true

- name: Configure git
run: |
git config --global user.name "${GIT_USER_NAME}"
git config --global user.email "${GIT_USER_EMAIL}"
env:
GIT_USER_NAME: ${{ steps.bot-details.outputs.name }}
GIT_USER_EMAIL: ${{ steps.bot-details.outputs.email }}
persist-credentials: false

- name: Ensure inputs.version is valid semver
run: |
Expand All @@ -68,9 +60,15 @@ jobs:
CHANNEL="$(echo ${{ inputs.version }} | cut -d '.' -f 1,2)"
echo "channel=$CHANNEL" >> "${GITHUB_OUTPUT}"

# The script creates the release commit through the GitHub API with the App token,
# so GitHub signs it and marks it as verified.
- name: Release samples
run: |
./samples/.github/scripts/release-samples.sh ${{ inputs.version }}
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
BOT_NAME: ${{ steps.bot-details.outputs.name }}
BOT_EMAIL: ${{ steps.bot-details.outputs.email }}

- name: Change the default branch
run: |
Expand Down