Skip to content

Commit 6cf28d7

Browse files
authored
Merge pull request #297 from tedjpoole/add-auto-merge-workflow
Added workflow to perform scheduled auto merge from upstream envoy
2 parents aaaa646 + 70ccc92 commit 6cf28d7

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Sync from Upstream (Scheduled)
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
schedule:
8+
- cron: "0 1 * * *"
9+
workflow_dispatch:
10+
11+
concurrency:
12+
group: ${{ github.workflow }}
13+
14+
jobs:
15+
sync:
16+
if: github.repository == 'envoyproxy/envoy-openssl'
17+
runs-on: ubuntu-22.04
18+
strategy:
19+
fail-fast: false
20+
matrix:
21+
branch_name:
22+
- release/v1.32
23+
- release/v1.28
24+
steps:
25+
- id: appauth
26+
uses: envoyproxy/toolshed/gh-actions/[email protected]
27+
with:
28+
key: ${{ secrets.ENVOY_CI_UPDATE_BOT_KEY }}
29+
app_id: ${{ secrets.ENVOY_CI_UPDATE_APP_ID }}
30+
31+
# Checkout the branch we're merging into
32+
- name: "Checkout ${{ github.repository }}[${{ matrix.branch_name }}]"
33+
uses: actions/checkout@v4
34+
with:
35+
token: ${{ steps.appauth.outputs.token }}
36+
ref: ${{ matrix.branch_name }}
37+
fetch-depth: 0
38+
39+
# Configure the git user info on the repository
40+
- run: git config user.name "${{ github.actor }}"
41+
- run: git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
42+
43+
# Checkout & run the script from the default branch
44+
- name: 'Checkout ci/envoy-sync-receive.sh'
45+
uses: actions/checkout@v4
46+
with:
47+
ref: ${{ github.event.repository.default_branch }}
48+
sparse-checkout: 'ci/envoy-sync-receive.sh'
49+
sparse-checkout-cone-mode: false
50+
path: '.script'
51+
- run: .script/ci/envoy-sync-receive.sh ${{ matrix.branch_name }}
52+
env:
53+
GH_TOKEN: ${{ steps.appauth.outputs.token }}

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,30 @@ repository, with the addition of:
3131
Note that the initial `release/v1.26` branch is *not* intended for production.
3232
It is anticipated that `release/v1.28` will be the first branch to reach production.
3333

34+
## Synchronizing
35+
36+
Since this repository is primarily a copy of the [envoyproxy/envoy](https://github.com/envoyproxy/envoy) repository,
37+
the process of keeping this repository in sync with the regular envoy repository is automated.
38+
39+
This automation is implemented by the
40+
[envoy-sync-scheduled.yaml](.github/workflows/envoy-sync-scheduled.yaml)
41+
workflow which executes on a daily schedule. For each of the branches listed in
42+
the workflow's strategy matrix, a merge is attempted from the [envoyproxy/envoy](https://github.com/envoyproxy/envoy)
43+
repository into the identically named branch in this repository.
44+
45+
- If the merge is successful, it:
46+
- pushes the feature branch to the repository
47+
- creates the associated pull request if it doesn't already exist
48+
- closes the associated issue if it already exists
49+
- If the merge is unsuccessful, it:
50+
- leaves the associated pull request untouched if it already exists
51+
- creates the associated issue issue if it doesn't already exist
52+
- adds a comment on the associated issue to describe the merge fail
53+
54+
Note that the feature branches created by this automation are named
55+
`auto-merge-<branch-name>`. These repeatable names allow the workflow to reuse &
56+
update existing branches and pull requests, rather than creating new ones each time.
57+
3458
## Building
3559

3660
The process for building envoy-openssl is very similar to building regular envoy, wherever possible

ci/envoy-sync-receive.sh

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Copyright Red Hat
4+
#
5+
# This script is invoked from .github/workflow/envoy-sync-*.yaml workflows.
6+
#
7+
# It merges the specified branch from the upstream envoyproxy/envoy repository
8+
# into the current branch in the current working directory. It is assumed that
9+
# the calling workflow has already checked out the destination repository and
10+
# switched to the destination branch, in the current working directory before
11+
# invoking us.
12+
#
13+
# - If the merge is successful, it:
14+
# - pushes the feature branch to the repository
15+
# - creates the associated pull request if it doesn't already exist
16+
# - closes the associated issue if it already exists
17+
#
18+
# -If the merge is unsuccessful, it:
19+
# - leaves the associated pull request untouched if it already exists
20+
# - creates the associated issue issue if it doesn't already exist
21+
# - adds a comment on the associated issue to describe the merge fail
22+
#
23+
24+
set -euo pipefail
25+
26+
notice() { printf "::notice:: %s\n" "$1"; }
27+
28+
SCRATCH="$(mktemp -d)"
29+
cleanup() {
30+
local savexit=$?
31+
rm -rf -- "${SCRATCH}"
32+
exit "${savexit}"
33+
}
34+
trap 'cleanup' EXIT
35+
36+
SRC_REPO_URL="https://github.com/envoyproxy/envoy.git"
37+
SRC_REPO_PATH="${SRC_REPO_URL/#*github.com?/}"
38+
SRC_REPO_PATH="${SRC_REPO_PATH/%.git/}"
39+
SRC_BRANCH_NAME="$1"
40+
SRC_HEAD_SHA="$(git ls-remote "${SRC_REPO_URL}" | awk "/\srefs\/heads\/${SRC_BRANCH_NAME/\//\\\/}$/{print \$1}")"
41+
42+
DST_REPO_URL=$(git remote get-url origin)
43+
DST_REPO_PATH="${DST_REPO_URL/#*github.com?/}"
44+
DST_REPO_PATH="${DST_REPO_PATH/%.git/}"
45+
DST_BRANCH_NAME=$(git branch --show-current)
46+
DST_HEAD_SHA=$(git rev-parse HEAD)
47+
48+
# Add the remote upstream repo and fetch the specified branch
49+
git remote remove upstream &> /dev/null || true
50+
git remote add -f -t "${SRC_BRANCH_NAME}" upstream "${SRC_REPO_URL}"
51+
52+
# Compose text for pull request or issue title
53+
TITLE="auto-merge ${SRC_REPO_PATH}[${SRC_BRANCH_NAME}] "
54+
TITLE+="into ${DST_REPO_PATH}[${DST_BRANCH_NAME}]"
55+
56+
# Create a new branch name for the merge. Deliberately don't include
57+
# any commit hash or timestamp in the name to ensure it is repeatable.
58+
# This ensures that each time we get invoked, due to an upstream change,
59+
# we accumulate the changes in the same branch and pull request, rather
60+
# than creating new ones that superceed the old one(s) each time.
61+
DST_NEW_BRANCH_NAME="auto-merge-$(echo "${SRC_BRANCH_NAME}" | tr /. -)"
62+
63+
# Set the default remote for the gh command
64+
gh repo set-default "${DST_REPO_PATH}"
65+
66+
# Perform the merge using --no-ff option to force creating a merge commit
67+
if git merge --no-ff --log=10000 --signoff -m "${TITLE}" \
68+
"upstream/${SRC_BRANCH_NAME}" > "${SCRATCH}/mergeout"; then
69+
DST_NEW_HEAD_SHA="$(git rev-parse HEAD)"
70+
if [[ "${DST_NEW_HEAD_SHA}" != "${DST_HEAD_SHA}" ]]; then
71+
git push --force origin "HEAD:${DST_NEW_BRANCH_NAME}"
72+
PR_COUNT=$(gh pr list --head "${DST_NEW_BRANCH_NAME}" \
73+
--base "${DST_BRANCH_NAME}" \
74+
--state open | wc -l)
75+
if [[ "${PR_COUNT}" == "0" ]]; then
76+
PR_URL=$(gh pr create --head "${DST_NEW_BRANCH_NAME}" \
77+
--base "${DST_BRANCH_NAME}" \
78+
--title "${TITLE}" \
79+
--body "Generated by $(basename "$0")")
80+
MERGE_OUTCOME="Created ${PR_URL}"
81+
else
82+
PR_ID=$(gh pr list --head "${DST_NEW_BRANCH_NAME}" \
83+
--base "${DST_BRANCH_NAME}" \
84+
--state open | head -1 | cut -f1)
85+
PR_URL="https://github.com/${DST_REPO_PATH}/pull/${PR_ID}"
86+
MERGE_OUTCOME="Updated ${PR_URL}"
87+
fi
88+
else
89+
MERGE_OUTCOME="No changes"
90+
fi
91+
notice "${TITLE} successful (${MERGE_OUTCOME})"
92+
# Close any related issues with a comment describing why
93+
for ISSUE_ID in $(gh issue list -S "${TITLE} failed" | cut -f1); do
94+
ISSUE_URL="https://github.com/${DST_REPO_PATH}/issues/${ISSUE_ID}"
95+
gh issue close "${ISSUE_URL}" --comment "Successful ${TITLE} (${MERGE_OUTCOME})"
96+
notice "Closed ${ISSUE_URL}"
97+
done
98+
else # merge fail
99+
notice "${TITLE} failed"
100+
ISSUE_COUNT=$(gh issue list -S "${TITLE} failed" | wc -l)
101+
if [[ "${ISSUE_COUNT}" == "0" ]]; then
102+
ISSUE_URL=$(gh issue create --title "${TITLE} failed" --body "${TITLE} failed")
103+
ISSUE_ID="$(basename "${ISSUE_URL}")"
104+
ISSUE_OUTCOME="Created"
105+
else
106+
ISSUE_ID="$(gh issue list -S "${TITLE} failed sort:created-asc" | tail -1 | cut -f1)"
107+
ISSUE_URL="https://github.com/${DST_REPO_PATH}/issues/${ISSUE_ID}"
108+
ISSUE_OUTCOME="Updated"
109+
fi
110+
COMMENT_URL=$(\
111+
gh issue comment "${ISSUE_URL}" --body-file - <<-EOF
112+
Failed to ${TITLE}
113+
114+
Upstream : [${SRC_HEAD_SHA}](https://github.com/${SRC_REPO_PATH}/commit/${SRC_HEAD_SHA})
115+
Downstream : [${DST_HEAD_SHA}](https://github.com/${DST_REPO_PATH}/commit/${DST_HEAD_SHA})
116+
117+
\`\`\`
118+
$(cat "${SCRATCH}/mergeout" || true)
119+
\`\`\`
120+
EOF
121+
)
122+
notice "${ISSUE_OUTCOME} ISSUE#${ISSUE_ID} (${COMMENT_URL})"
123+
exit 1
124+
fi

0 commit comments

Comments
 (0)