Skip to content

Commit 63896c1

Browse files
NO-JIRA: Add workflow to update openshift/loki
1 parent 2f8c19a commit 63896c1

2 files changed

Lines changed: 292 additions & 0 deletions

File tree

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
name: Common logging sync main flow
2+
on:
3+
workflow_call:
4+
inputs:
5+
upstream:
6+
description: Upstream repo path in owner/repo format
7+
required: true
8+
type: string
9+
downstream:
10+
description: Downstream repo path in owner/repo format
11+
required: true
12+
type: string
13+
downstream-branch:
14+
description: Downstream branch to sync into
15+
required: false
16+
default: main
17+
type: string
18+
sandbox:
19+
description: Sandbox repo path in owner/repo format. Used as a base to create PR against downstream.
20+
required: true
21+
type: string
22+
commit-filter:
23+
description: Grep pattern to filter incoming commits. When set, a PR is only created if matching commits exist. When empty, always syncs.
24+
required: false
25+
default: ''
26+
type: string
27+
restore-upstream:
28+
description: List of files to be reset using upstream content on merge conflict.
29+
required: false
30+
default: ''
31+
type: string
32+
restore-downstream:
33+
description: List of files to be reset using downstream content on merge conflict.
34+
required: false
35+
default: ''
36+
type: string
37+
secrets:
38+
cloner-app-id:
39+
description: Github ID of cloner app
40+
required: true
41+
cloner-app-private-key:
42+
description: Github private key of cloner app
43+
required: true
44+
pr-app-id:
45+
description: Github ID of PR creation app
46+
required: true
47+
pr-app-private-key:
48+
description: Github private key of PR creation app
49+
required: true
50+
slack-webhook-url:
51+
description: Slack webhook URL to send notification
52+
required: true
53+
54+
jobs:
55+
sync:
56+
runs-on: ubuntu-latest
57+
name: Sync main branch
58+
steps:
59+
- name: Find github org name from repo name
60+
id: org
61+
run: |
62+
{
63+
echo "upstream=$(dirname ${{ inputs.upstream }})"
64+
echo "downstream=$(dirname ${{ inputs.downstream }})"
65+
echo "sandbox=$(dirname ${{ inputs.sandbox }})"
66+
} >> "$GITHUB_OUTPUT"
67+
68+
- uses: actions/checkout@v6
69+
with:
70+
repository: ${{ inputs.downstream }}
71+
fetch-depth: 0
72+
ref: ${{ inputs.downstream-branch }}
73+
74+
- name: Configure git
75+
run: |
76+
git config user.name 'github-actions[bot]'
77+
git config user.email 'github-actions[bot]@users.noreply.github.com'
78+
79+
- name: Fetch upstream ${{ inputs.downstream-branch }}
80+
run: git fetch https://github.com/${{ inputs.upstream }} ${{ inputs.downstream-branch }}
81+
82+
- name: Check if behind upstream
83+
id: check
84+
run: |
85+
BEHIND=$(git rev-list --count HEAD..FETCH_HEAD)
86+
echo "behind=${BEHIND}" >> "$GITHUB_OUTPUT"
87+
if [ "${BEHIND}" -eq 0 ]; then
88+
echo "::notice::Already up-to-date with upstream"
89+
else
90+
echo "::notice::${BEHIND} commits behind upstream"
91+
fi
92+
93+
- name: Filter incoming commits
94+
if: steps.check.outputs.behind != '0'
95+
id: filter
96+
run: |
97+
if [ -n "${{ inputs.commit-filter }}" ]; then
98+
COMMITS=$(git log --oneline HEAD..FETCH_HEAD --grep='${{ inputs.commit-filter }}' --extended-regexp)
99+
if [ -z "$COMMITS" ]; then
100+
echo "has_matches=false" >> "$GITHUB_OUTPUT"
101+
echo "::notice::No commits matching filter '${{ inputs.commit-filter }}' — skipping PR"
102+
exit 0
103+
fi
104+
echo "has_matches=true" >> "$GITHUB_OUTPUT"
105+
{
106+
echo 'commits<<EOF'
107+
echo "$COMMITS"
108+
echo 'EOF'
109+
} >> "$GITHUB_OUTPUT"
110+
else
111+
echo "has_matches=true" >> "$GITHUB_OUTPUT"
112+
fi
113+
114+
- name: Merge upstream ${{ inputs.downstream-branch }}
115+
if: steps.check.outputs.behind != '0' && steps.filter.outputs.has_matches == 'true'
116+
id: merge
117+
run: |
118+
git merge FETCH_HEAD --no-edit || echo 'MERGE_CONFLICT=true' >> "$GITHUB_OUTPUT"
119+
120+
- name: Resolve conflict using upstream contents
121+
if: steps.merge.outputs.MERGE_CONFLICT == 'true' && inputs.restore-upstream != ''
122+
run: |
123+
echo "reset ${{ inputs.restore-upstream }}"
124+
git checkout --theirs ${{ inputs.restore-upstream }} || true
125+
git add ${{ inputs.restore-upstream }} || true
126+
127+
- name: Remove deleted files
128+
if: steps.merge.outputs.MERGE_CONFLICT == 'true'
129+
run: |
130+
git diff --name-only --diff-filter=D | xargs -r git rm
131+
132+
- name: Resolve conflict using downstream contents
133+
if: steps.merge.outputs.MERGE_CONFLICT == 'true' && inputs.restore-downstream != ''
134+
run: |
135+
echo "reset ${{ inputs.restore-downstream }}"
136+
git checkout --ours ${{ inputs.restore-downstream }}
137+
git add ${{ inputs.restore-downstream }}
138+
139+
- name: Resolve conflict due to deleted downstream files
140+
if: steps.merge.outputs.MERGE_CONFLICT == 'true'
141+
run: |
142+
git status --porcelain | awk '{ if ($1=="UD" || $1=="DU") print $2 }' | xargs -I {} git rm {}
143+
144+
- name: Continue after merge conflict
145+
if: steps.merge.outputs.MERGE_CONFLICT == 'true'
146+
run: |
147+
git add -A
148+
git merge --continue
149+
150+
- name: Get auth token to create pull request for ${{ inputs.downstream }}
151+
if: github.event_name != 'pull_request' && steps.check.outputs.behind != '0' && steps.filter.outputs.has_matches == 'true'
152+
id: pr
153+
uses: getsentry/action-github-app-token@v3
154+
with:
155+
app_id: ${{ secrets.pr-app-id }}
156+
private_key: ${{ secrets.pr-app-private-key }}
157+
scope: ${{ steps.org.outputs.downstream }}
158+
159+
- name: Get auth token to push to ${{ inputs.sandbox }}
160+
if: github.event_name != 'pull_request' && steps.check.outputs.behind != '0' && steps.filter.outputs.has_matches == 'true'
161+
id: cloner
162+
uses: getsentry/action-github-app-token@v3
163+
with:
164+
app_id: ${{ secrets.cloner-app-id }}
165+
private_key: ${{ secrets.cloner-app-private-key }}
166+
scope: ${{ steps.org.outputs.sandbox }}
167+
168+
- name: Create Pull Request
169+
if: github.event_name != 'pull_request' && steps.check.outputs.behind != '0' && steps.filter.outputs.has_matches == 'true'
170+
uses: rhobs/create-pull-request@v4
171+
id: create-pr
172+
with:
173+
title: "[Logging Sync Bot] Sync ${{ inputs.downstream }} with ${{ inputs.upstream }} main"
174+
body: |
175+
## Description
176+
Automated sync of `${{ inputs.upstream }}` main into `${{ inputs.downstream }}` ${{ inputs.downstream-branch }}.
177+
The logs for this run can be found [in the syncbot repo actions](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}).
178+
179+
${{ inputs.commit-filter != '' && format('## Commits matching `{0}`', inputs.commit-filter) || '' }}
180+
${{ steps.filter.outputs.commits || '' }}
181+
author: 'github-actions[bot]<github-actions[bot]@users.noreply.github.com>'
182+
committer: 'github-actions[bot]<github-actions[bot]@users.noreply.github.com>'
183+
signoff: true
184+
branch: automated-sync-main-${{ inputs.downstream-branch }}
185+
delete-branch: true
186+
token: ${{ steps.pr.outputs.token }}
187+
push-to-fork: ${{ inputs.sandbox }}
188+
push-to-fork-token: ${{ steps.cloner.outputs.token }}
189+
190+
- name: Check if PR exists using gh cli
191+
if: github.event_name != 'pull_request' && failure()
192+
id: pr-exists
193+
env:
194+
GH_TOKEN: ${{ steps.pr.outputs.token }}
195+
run: |
196+
if [ "${{ steps.create-pr.outcome }}" != "success" ]; then
197+
PR_URL=$(gh pr list --json url --jq '.[0].url' --repo ${{ inputs.downstream }} --state open --head automated-sync-main-${{ inputs.downstream-branch }})
198+
if [ ! -z "$PR_URL" ]; then
199+
echo "pr_exists=1" >> "$GITHUB_OUTPUT"
200+
echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT"
201+
echo "PR exists >> $PR_URL"
202+
else
203+
echo "pr_exists=0" >> "$GITHUB_OUTPUT"
204+
fi
205+
fi
206+
207+
- name: Compose slack message body
208+
if: github.event_name != 'pull_request' && (success() || steps.check.outputs.behind == '0' || steps.filter.outputs.has_matches == 'false' || steps.pr-exists.outputs.pr_exists == '1')
209+
continue-on-error: true
210+
id: slack-message
211+
run: |
212+
if [ "${{ steps.pr-exists.outputs.pr_exists }}" == "1" ]; then
213+
PR_URL="${{ steps.pr-exists.outputs.pr_url }}"
214+
else
215+
PR_URL="${{ steps.create-pr.outputs.pull-request-url }}"
216+
fi
217+
if [ "${{ steps.check.outputs.behind }}" == "0" ]; then
218+
echo "message=${{ inputs.downstream }} is already up-to-date with ${{ inputs.upstream }} main." >> "$GITHUB_OUTPUT"
219+
elif [ "${{ steps.filter.outputs.has_matches }}" == "false" ]; then
220+
echo "message=${{ inputs.downstream }} has no commits matching filter '${{ inputs.commit-filter }}' — skipped." >> "$GITHUB_OUTPUT"
221+
else
222+
echo "message=PR $PR_URL has been ${{ steps.create-pr.outputs.pull-request-operation || 'updated' }}." >> "$GITHUB_OUTPUT"
223+
fi
224+
225+
- uses: 8398a7/action-slack@v3
226+
if: github.event_name != 'pull_request' && (success() || steps.check.outputs.behind == '0' || steps.filter.outputs.has_matches == 'false' || steps.pr-exists.outputs.pr_exists == '1')
227+
continue-on-error: true
228+
with:
229+
status: custom
230+
fields: workflow
231+
custom_payload: |
232+
{
233+
attachments: [{
234+
color: 'good',
235+
text: `${process.env.AS_WORKFLOW}\n ${{ steps.slack-message.outputs.message }}`,
236+
}]
237+
}
238+
env:
239+
SLACK_WEBHOOK_URL: ${{ secrets.slack-webhook-url }}
240+
241+
- uses: 8398a7/action-slack@v3
242+
if: github.event_name != 'pull_request' && (failure() && steps.check.outputs.behind != '0' && steps.filter.outputs.has_matches != 'false' && !(steps.pr-exists.outputs.pr_exists == '1'))
243+
continue-on-error: true
244+
with:
245+
status: custom
246+
fields: workflow
247+
custom_payload: |
248+
{
249+
attachments: [{
250+
color: 'danger',
251+
text: `${process.env.AS_WORKFLOW} has failed.`,
252+
}]
253+
}
254+
env:
255+
SLACK_WEBHOOK_URL: ${{ secrets.slack-webhook-url }}

.github/workflows/merge-loki.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Loki sync
2+
3+
on:
4+
workflow_dispatch:
5+
schedule:
6+
- cron: '0 0 * * *' #@daily
7+
pull_request:
8+
paths:
9+
- '.github/workflows/logging-sync-main-flow.yaml'
10+
- '.github/workflows/merge-loki.yaml'
11+
push:
12+
paths:
13+
- '.github/workflows/logging-sync-main-flow.yaml'
14+
- '.github/workflows/merge-loki.yaml'
15+
jobs:
16+
loki-sync:
17+
uses: ./.github/workflows/logging-sync-main-flow.yaml
18+
with:
19+
upstream: grafana/loki
20+
downstream: openshift/loki
21+
sandbox: rhobs/loki
22+
commit-filter: '(operator):'
23+
restore-upstream: >-
24+
CHANGELOG.md
25+
VERSION
26+
go.mod
27+
go.sum
28+
restore-downstream: >-
29+
OWNERS
30+
Dockerfile.ocp
31+
Dockerfile.promtail.ocp
32+
secrets:
33+
pr-app-id: ${{ secrets.APP_ID }}
34+
pr-app-private-key: ${{ secrets.APP_PRIVATE_KEY }}
35+
cloner-app-id: ${{ secrets.CLONER_APP_ID }}
36+
cloner-app-private-key: ${{ secrets.CLONER_APP_PRIVATE_KEY }}
37+
slack-webhook-url: ${{ secrets.LOGGING_SLACK_WEBHOOK_URL }}

0 commit comments

Comments
 (0)