Skip to content

Commit 2070919

Browse files
ci: PLT-506: Follow Merge v2
1 parent f7394db commit 2070919

File tree

3 files changed

+348
-41
lines changed

3 files changed

+348
-41
lines changed

.github/workflows/fm-command.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: "/fm command"
2+
3+
on:
4+
repository_dispatch:
5+
types: [ fm-command ]
6+
7+
concurrency:
8+
group: ${{ github.workflow }}-${{ github.event.client_payload.github.payload.issue.number }}-${{ github.event.client_payload.slash_command.command }}-${{ github.event.client_payload.slash_command.args.unnamed.arg1 || github.event.client_payload.slash_command.args.all }}
9+
10+
jobs:
11+
sync:
12+
name: "Update: Update Feature Flags"
13+
if: github.event.client_payload.slash_command.args.unnamed.arg1 == 'sync'
14+
uses: ./.github/workflows/follow-merge-upstream-repo-sync-v2.yml
15+
with:
16+
branch_name: ${{ github.event.client_payload.pull_request.head.ref }}
17+
secrets: inherit
18+
19+
help:
20+
if: ${{ github.event.client_payload.slash_command.args.unnamed.arg1 == 'help' || !contains(fromJson('["sync"]'), github.event.client_payload.slash_command.args.unnamed.arg1) }}
21+
runs-on: ubuntu-latest
22+
timeout-minutes: 1
23+
steps:
24+
- name: Update comment
25+
uses: peter-evans/create-or-update-comment@v4
26+
with:
27+
token: ${{ secrets.GIT_PAT }}
28+
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
29+
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
30+
body: |
31+
> Command | Description
32+
> --- | ---
33+
> /fm sync | Sync upstream prs and merge with pull request base
34+
reaction-type: hooray
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
name: 'Follow Merge: Upstream repo sync v2'
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
branch_name:
7+
required: true
8+
type: string
9+
workflow_dispatch:
10+
inputs:
11+
branch_name:
12+
description: 'Branch name'
13+
required: true
14+
type: string
15+
16+
concurrency:
17+
group: ${{ github.workflow }}-${{ inputs.branch_name }}
18+
cancel-in-progress: true
19+
20+
env:
21+
NODE: "18"
22+
YARN: "1.22"
23+
24+
jobs:
25+
sync:
26+
name: Sync PR
27+
runs-on: ubuntu-latest
28+
outputs:
29+
adala: "${{ steps.upstream-prs.outputs.adala }}"
30+
label-studio-query-vectordb: "${{ steps.upstream-prs.outputs.label-studio-query-vectordb }}"
31+
steps:
32+
- uses: hmarr/[email protected]
33+
34+
- name: Add Workflow link to chat ops command comment
35+
if: github.event.client_payload.github.payload.comment.id && github.event.client_payload.github.payload.repository.full_name
36+
uses: peter-evans/create-or-update-comment@v4
37+
with:
38+
token: ${{ secrets.GIT_PAT }}
39+
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
40+
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
41+
body: |
42+
> [Workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
43+
44+
- name: Checkout Actions Hub
45+
uses: actions/checkout@v4
46+
with:
47+
token: ${{ secrets.GIT_PAT }}
48+
repository: HumanSignal/actions-hub
49+
path: ./.github/actions-hub
50+
51+
- name: Find or Create branch
52+
uses: ./.github/actions-hub/actions/github-find-or-create-branch
53+
id: get-branch
54+
with:
55+
github_token: ${{ secrets.GIT_PAT }}
56+
branch_name: "${{ inputs.branch_name }}"
57+
58+
- name: Checkout
59+
uses: actions/checkout@v4
60+
with:
61+
token: ${{ secrets.GIT_PAT }}
62+
ref: ${{ steps.get-branch.outputs.branch_name }}
63+
fetch-depth: 0
64+
65+
- name: Checkout Actions Hub
66+
uses: actions/checkout@v4
67+
with:
68+
token: ${{ secrets.GIT_PAT }}
69+
repository: HumanSignal/actions-hub
70+
path: ./.github/actions-hub
71+
72+
- name: Get Upstream PRs
73+
id: upstream-prs
74+
uses: actions/github-script@v7
75+
env:
76+
BRANCH_NAME: ${{ inputs.branch_name }}
77+
with:
78+
github-token: ${{ secrets.GIT_PAT }}
79+
script: |
80+
const { repo, owner } = context.repo;
81+
const branch_name = process.env.BRANCH_NAME;
82+
const pyProjectPath = "pyproject.toml";
83+
const repos = ["label-studio-client-generator"];
84+
const repos_infra = [];
85+
const base_branch_name = 'develop';
86+
87+
let shas = {};
88+
89+
// GET UPSTREAM PRS
90+
let upstream_pulls = [];
91+
for (const repo of repos.concat(repos_infra)) {
92+
const {data: pulls} = await github.rest.pulls.list({
93+
owner,
94+
repo,
95+
state: "all",
96+
head: `${owner}:${branch_name}`,
97+
});
98+
const first_open_pull = pulls.find(e => e.state === 'open');
99+
const pull = first_open_pull || pulls[0];
100+
if (pull) {
101+
core.info(`PRs found for ${repo} ${pull.html_url} ${pull.merged_at ? 'merged' : pull.state} ${pull.merged_at ? pull.merge_commit_sha : pull.head.sha}`)
102+
upstream_pulls.push(pull);
103+
if (pull.merged_at) {
104+
shas[repo] = pull.merge_commit_sha;
105+
} else if (pull.state === 'open') {
106+
shas[repo] = pull.head.sha;
107+
}
108+
} else {
109+
core.notice(`No open upstream PRs found for ${repo}`)
110+
}
111+
}
112+
113+
// GET BASE VERSIONS
114+
const {data: pyprojectBlob} = await github.rest.repos.getContent({
115+
owner: owner,
116+
repo: repo,
117+
ref: base_branch_name,
118+
path: pyProjectPath,
119+
});
120+
const base_pyproject = Buffer.from(pyprojectBlob.content, pyprojectBlob.encoding).toString("utf8");
121+
for (const repo of repos) {
122+
const match = base_pyproject.match(new RegExp(`${repo}\/archive.*(?<sha>[a-f0-9]{40})`));
123+
if (match && match.groups.sha) {
124+
core.info(`Base version for ${repo} ${pyprojectBlob.html_url} ${match.groups.sha}`);
125+
shas[repo] = shas[repo] || match.groups.sha;
126+
} else {
127+
core.setFailed(`Could not parse ${repo} version from ${pyprojectBlob.html_url}`);
128+
}
129+
}
130+
131+
for (const [key, value] of Object.entries(shas)) {
132+
core.setOutput(key, value);
133+
}
134+
135+
core.info(`Base branch name ${base_branch_name}`);
136+
core.setOutput('base_branch_name', base_branch_name);
137+
138+
if (upstream_pulls.length > 0) {
139+
core.info(`Title ${upstream_pulls[0].title}`);
140+
core.setOutput('title', upstream_pulls[0].title);
141+
}
142+
143+
const upstream_prs_urls = upstream_pulls.map(e => e.html_url).join(',');
144+
core.info(`Upstream PRs URLs ${upstream_prs_urls}`);
145+
core.setOutput('upstream_prs_urls', upstream_prs_urls);
146+
147+
let assignees = [];
148+
for (const pull of upstream_pulls) {
149+
if (pull.user) assignees.push(pull.user.login);
150+
if (pull.assignee) assignees.push(pull.assignee.login);
151+
assignees.concat(pull.assignees.map(e => e.name));
152+
}
153+
assignees = assignees.filter(x => x !== 'robot-ci-heartex');
154+
core.info(`Assignees ${assignees.join(',')}`);
155+
core.setOutput('assignees', assignees.join(','));
156+
157+
if (assignees.length > 0) {
158+
const author_username = assignees[0];
159+
core.info(`Author username ${author_username}`);
160+
core.setOutput('author_username', author_username);
161+
}
162+
163+
let status = "open"
164+
if (upstream_pulls.every(p => p.merged_at)) {
165+
status = 'merged';
166+
} else if (upstream_pulls.every(p => p.closed_at)) {
167+
status = 'closed';
168+
}
169+
core.info(`Status: ${status}`);
170+
core.setOutput("status", status);
171+
172+
- name: Git Configure
173+
uses: ./.github/actions-hub/actions/git-configure
174+
with:
175+
username: ${{ steps.upstream-prs.outputs.author_username }}
176+
177+
- name: Setup node
178+
uses: actions/setup-node@v4
179+
180+
- name: Install Fern
181+
run: npm install -g fern-api@latest
182+
183+
- name: Download Fern
184+
run: npm install -g fern-api@latest
185+
186+
- name: Set Fern Generator Branch and Mode
187+
env:
188+
BRANCH_NAME: "${{ steps.get-branch.outputs.branch_name }}"
189+
FERN_GENERATOR_PATH: "fern/generators.yml"
190+
run: |
191+
yq e --inplace ".groups.python-sdk-staging.generators[0].github.branch |= \"${BRANCH_NAME}\"" "${FERN_GENERATOR_PATH}"
192+
yq e --inplace ".groups.python-sdk-staging.generators[0].github.mode |= \"push\"" "${FERN_GENERATOR_PATH}"
193+
cat "${FERN_GENERATOR_PATH}"
194+
195+
- name: Check Fern API is valid
196+
run: fern check
197+
198+
- name: Find or Create PR
199+
uses: ./.github/actions-hub/actions/follow-merge-find-or-create-pull-request
200+
id: get-pr
201+
with:
202+
github_token: ${{ secrets.GIT_PAT }}
203+
branch_name: "${{ steps.get-branch.outputs.branch_name }}"
204+
title: "${{ steps.upstream-prs.outputs.title }}"
205+
upstream_prs_urls: "${{ steps.upstream-prs.outputs.upstream_prs_urls }}"
206+
207+
- name: Add PR Assignees
208+
if: steps.upstream-prs.outputs.assignees
209+
uses: ./.github/actions-hub/actions/github-add-pull-request-assignees
210+
continue-on-error: true
211+
with:
212+
github_token: ${{ secrets.GIT_PAT }}
213+
pullrequest_number: "${{ steps.get-pr.outputs.number }}"
214+
assignees: "${{ steps.upstream-prs.outputs.assignees }}"
215+
216+
- name: Convert to ready for review
217+
if: steps.upstream-prs.outputs.status == 'merged'
218+
id: ready-for-review-pr
219+
shell: bash
220+
env:
221+
GIT_PAT: ${{ secrets.GIT_PAT }}
222+
run: |
223+
echo "$GIT_PAT" | gh auth login --with-token
224+
gh api graphql -F id='${{ steps.get-pr.outputs.node_id }}' -f query='
225+
mutation($id: ID!) {
226+
markPullRequestReadyForReview(input: { pullRequestId: $id }) {
227+
pullRequest {
228+
id
229+
}
230+
}
231+
}
232+
'
233+
234+
- name: Enable AutoMerge
235+
if: steps.upstream-prs.outputs.status == 'merged'
236+
continue-on-error: true
237+
shell: bash
238+
env:
239+
GIT_PAT: ${{ secrets.GIT_PAT }}
240+
run: |
241+
echo "$GIT_PAT" | gh auth login --with-token
242+
gh api graphql -f pull='${{ steps.get-pr.outputs.node_id }}' -f query='
243+
mutation($pull: ID!) {
244+
enablePullRequestAutoMerge(input: {pullRequestId: $pull, mergeMethod: SQUASH}) {
245+
pullRequest {
246+
id
247+
number
248+
}
249+
}
250+
}'
251+
252+
- name: Close PR
253+
if: steps.upstream-prs.outputs.status == 'closed'
254+
continue-on-error: true
255+
shell: bash
256+
env:
257+
GIT_PAT: ${{ secrets.GIT_PAT }}
258+
run: |
259+
echo "$GIT_PAT" | gh auth login --with-token
260+
gh api graphql -f pull='${{ steps.get-pr.outputs.node_id }}' -f query='
261+
mutation($pull: ID!) {
262+
closePullRequest(input: {pullRequestId: $pull }) {
263+
pullRequest {
264+
id
265+
state
266+
}
267+
}
268+
}'
269+
270+
# - name: Notify on failure
271+
# uses: ./.github/actions-hub/actions/github-create-comment
272+
# if: failure()
273+
# with:
274+
# github_token: ${{ secrets.GIT_PAT }}
275+
# repository: "${{ steps.fm.outputs.repo_name }}"
276+
# issue_number: "${{ steps.fm.outputs.pr_number }}"
277+
# body: |
278+
# Follow Merge downstream workflow has been failed.
279+
# > [Workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
280+
281+
- name: Add reaction to chat ops command comment
282+
if: always() && github.event.client_payload.github.payload.comment.id && github.event.client_payload.github.payload.repository.full_name
283+
uses: peter-evans/create-or-update-comment@v4
284+
with:
285+
token: ${{ secrets.GIT_PAT }}
286+
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
287+
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
288+
reactions: ${{ job.status == 'success' && '+1' || '-1' }}

0 commit comments

Comments
 (0)