Skip to content

Commit 9640d01

Browse files
committed
ci(release): sync with remix (#9813)
* ci(release): sync with remix * ci: sync with remix * Delete postrelease.yml * ci: sync latest changes around getting previous release (cherry picked from commit a80a62d)
1 parent de3c96a commit 9640d01

File tree

7 files changed

+159
-87
lines changed

7 files changed

+159
-87
lines changed

.github/workflows/postrelease.yml

-17
This file was deleted.

.github/workflows/release.yml

+50-21
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,39 @@
1-
name: 🕊 Release
1+
name: 🦋 Changesets Release
22
on:
33
push:
44
branches:
55
- release
66
- "release-*"
77
- "!release-experimental"
88
- "!release-experimental-*"
9-
10-
concurrency: ${{ github.workflow }}-${{ github.ref }}
11-
12-
env:
13-
CI: true
9+
- "!release-manual"
10+
- "!release-manual-*"
1411

1512
jobs:
1613
release:
1714
name: 🦋 Changesets Release
1815
if: github.repository == 'remix-run/react-router'
1916
runs-on: ubuntu-latest
20-
17+
outputs:
18+
publishedPackages: ${{ steps.changesets.outputs.publishedPackages }}
19+
published: ${{ steps.changesets.outputs.published }}
2120
steps:
21+
- name: 🛑 Cancel Previous Runs
22+
uses: styfle/[email protected]
23+
2224
- name: ⬇️ Checkout repo
2325
uses: actions/checkout@v3
2426
with:
2527
fetch-depth: 0
2628

27-
- name: ⎔ Setup Node
29+
- name: ⎔ Setup node
2830
uses: actions/setup-node@v3
2931
with:
3032
node-version-file: ".nvmrc"
31-
cache: yarn
33+
cache: "yarn"
3234

33-
- name: 📥 Install dependencies
34-
# even though this is called "npm-install" it does use yarn to install
35-
# because we have a yarn.lock and caches efficiently.
36-
uses: bahmutov/npm-install@v1
35+
- name: 📥 Install deps
36+
run: yarn --frozen-lockfile
3737

3838
- name: 🔐 Setup npm auth
3939
run: |
@@ -52,16 +52,45 @@ jobs:
5252
version: yarn run version
5353
commit: "chore: Update version for release"
5454
title: "chore: Update version for release"
55-
publish: yarn release
55+
publish: yarn run release
5656
createGithubReleases: false
5757
env:
5858
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN_SO_OTHER_ACTIONS_RUN }}
5959
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
6060

61-
# comment:
62-
# needs: [release]
63-
# name: 📝 Comment on related issues and pull requests
64-
# if: github.repository == 'remix-run/react-router'
65-
# uses: remix-run/react-router/.github/workflows/release-comments.yml@main
66-
# with:
67-
# ref: ${{ github.ref }}
61+
findPackage:
62+
name: 🦋 Find Package
63+
needs: [release]
64+
runs-on: ubuntu-latest
65+
if: github.repository == 'remix-run/react-router' && needs.release.outputs.published == 'true'
66+
outputs:
67+
package: ${{ steps.findPackage.outputs.package }}
68+
steps:
69+
- name: 🛑 Cancel Previous Runs
70+
uses: styfle/[email protected]
71+
72+
- name: ⬇️ Checkout repo
73+
uses: actions/checkout@v3
74+
75+
- name: ⎔ Setup node
76+
uses: actions/setup-node@v3
77+
with:
78+
node-version: 16
79+
cache: "npm"
80+
81+
- id: findPackage
82+
run: |
83+
package=$(node ./scripts/release/find-release-from-changeset.js)
84+
echo "package=${package}" >> $GITHUB_OUTPUT
85+
env:
86+
packageVersionToFollow: "react-router"
87+
publishedPackages: ${{ needs.release.outputs.publishedPackages }}
88+
89+
comment:
90+
name: 📝 Comment on related issues and pull requests
91+
if: github.repository == 'remix-run/react-router' && needs.findPackage.outputs.package != ''
92+
needs: [release, findPackage]
93+
uses: ./.github/workflows/release-comments.yml
94+
with:
95+
ref: refs/tags/${{ needs.findPackage.outputs.package }}
96+
packageVersionToFollow: "react-router"

scripts/release/comment.ts

+15-26
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
OWNER,
44
REPO,
55
PR_FILES_STARTS_WITH,
6-
IS_NIGHTLY_RELEASE,
6+
IS_STABLE_RELEASE,
77
AWAITING_RELEASE_LABEL,
88
} from "./constants";
99
import {
@@ -53,7 +53,7 @@ async function commentOnIssuesAndPrsAboutRelease() {
5353
let prLabels = pr.labels.map((label) => label.name);
5454
let prIsAwaitingRelease = prLabels.includes(AWAITING_RELEASE_LABEL);
5555

56-
if (!IS_NIGHTLY_RELEASE && prIsAwaitingRelease) {
56+
if (IS_STABLE_RELEASE && prIsAwaitingRelease) {
5757
promises.push(
5858
removeLabel({ owner: OWNER, repo: REPO, issue: pr.number })
5959
);
@@ -73,27 +73,19 @@ async function commentOnIssuesAndPrsAboutRelease() {
7373

7474
issuesCommentedOn.add(issue.number);
7575
let issueUrl = getGitHubUrl("issue", issue.number);
76+
console.log(`commenting on issue ${issueUrl}`);
7677

77-
if (IS_NIGHTLY_RELEASE || !prIsAwaitingRelease) {
78-
console.log(`commenting on ${issueUrl}`);
79-
promises.push(
80-
commentOnIssue({
81-
owner: OWNER,
82-
repo: REPO,
83-
issue: issue.number,
84-
version: VERSION,
85-
})
86-
);
87-
} else {
88-
console.log(`commenting on and closing ${issueUrl}`);
89-
promises.push(
90-
commentOnIssue({
91-
owner: OWNER,
92-
repo: REPO,
93-
issue: issue.number,
94-
version: VERSION,
95-
})
96-
);
78+
promises.push(
79+
commentOnIssue({
80+
owner: OWNER,
81+
repo: REPO,
82+
issue: issue.number,
83+
version: VERSION,
84+
})
85+
);
86+
87+
if (IS_STABLE_RELEASE) {
88+
console.log(`closing issue ${issueUrl}`);
9789
promises.push(
9890
closeIssue({ owner: OWNER, repo: REPO, issue: issue.number })
9991
);
@@ -104,10 +96,7 @@ async function commentOnIssuesAndPrsAboutRelease() {
10496
let result = await Promise.allSettled(promises);
10597
let rejected = result.filter((r) => r.status === "rejected");
10698
if (rejected.length > 0) {
107-
console.log(
108-
"🚨 failed to comment on some issues/prs - the most likely reason is they were issues that were turned into discussions, which don't have an api to comment with"
109-
);
110-
console.log(rejected);
99+
console.error("🚨 failed to comment on some issues/prs", rejected);
111100
}
112101
}
113102

scripts/release/constants.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { cleanupRef, cleanupTagName, isNightly } from "./utils";
1+
import { cleanupRef, cleanupTagName, isNightly, isStable } from "./utils";
22

33
if (!process.env.DEFAULT_BRANCH) {
44
throw new Error("DEFAULT_BRANCH is required");
@@ -32,3 +32,4 @@ export const NIGHTLY_BRANCH = process.env.NIGHTLY_BRANCH;
3232
export const PR_FILES_STARTS_WITH = ["packages/"];
3333
export const IS_NIGHTLY_RELEASE = isNightly(VERSION);
3434
export const AWAITING_RELEASE_LABEL = "awaiting release";
35+
export const IS_STABLE_RELEASE = isStable(VERSION);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
*
3+
* @param {string | undefined} publishedPackages
4+
* @param {string | undefined} packageVersionToFollow
5+
* @returns {string | undefined}
6+
*/
7+
function findReleaseFromChangeset(publishedPackages, packageVersionToFollow) {
8+
if (!publishedPackages) {
9+
throw new Error("No published packages found");
10+
}
11+
12+
let packages = JSON.parse(publishedPackages);
13+
14+
if (!Array.isArray(packages)) {
15+
throw new Error("Published packages is not an array");
16+
}
17+
18+
/** @see https://github.com/changesets/action#outputs */
19+
/** @type { { name: string; version: string }[] } */
20+
let typed = packages.filter((pkg) => "name" in pkg && "version" in pkg);
21+
22+
let found = typed.find((pkg) => pkg.name === packageVersionToFollow);
23+
24+
if (!found) {
25+
throw new Error(
26+
`${packageVersionToFollow} was not found in the published packages`
27+
);
28+
}
29+
30+
let result = `${found.name}@${found.version}`;
31+
console.log(result);
32+
return result;
33+
}
34+
35+
findReleaseFromChangeset(
36+
process.env.publishedPackages,
37+
process.env.packageVersionToFollow
38+
);

scripts/release/github.ts

+48-22
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import {
77
DEFAULT_BRANCH,
88
PACKAGE_VERSION_TO_FOLLOW,
99
AWAITING_RELEASE_LABEL,
10+
IS_NIGHTLY_RELEASE,
11+
IS_STABLE_RELEASE,
1012
} from "./constants";
1113
import { gql, graphqlWithAuth, octokit } from "./octokit";
1214
import type { MinimalTag } from "./utils";
15+
import { isNightly, isStable } from "./utils";
1316
import { cleanupTagName } from "./utils";
1417
import { checkIfStringStartsWith } from "./utils";
1518

@@ -140,34 +143,32 @@ function getPreviousTagFromCurrentTag(
140143

141144
return { tag: tagName, date, isPrerelease };
142145
})
143-
.filter((v: any): v is MinimalTag => typeof v !== "undefined");
146+
.filter((v: any): v is MinimalTag => typeof v !== "undefined")
147+
.filter((tag) => {
148+
if (IS_STABLE_RELEASE) return isStable(tag.tag);
149+
let isNightlyTag = isNightly(tag.tag);
150+
if (IS_NIGHTLY_RELEASE) return isNightlyTag;
151+
return !isNightlyTag;
152+
})
153+
.sort((a, b) => {
154+
if (IS_NIGHTLY_RELEASE) {
155+
return b.date.getTime() - a.date.getTime();
156+
}
157+
158+
return semver.rcompare(a.tag, b.tag);
159+
});
144160

145161
let currentTagIndex = validTags.findIndex((tag) => tag.tag === currentTag);
146162
let currentTagInfo: MinimalTag | undefined = validTags.at(currentTagIndex);
147163
let previousTagInfo: MinimalTag | undefined;
148164

149165
if (!currentTagInfo) {
150-
throw new Error(`Could not find last tag ${currentTag}`);
151-
}
152-
153-
// if the currentTag was a stable tag, then we want to find the previous stable tag
154-
if (!currentTagInfo.isPrerelease) {
155-
validTags = validTags
156-
.filter((tag) => !tag.isPrerelease)
157-
.sort((a, b) => semver.rcompare(a.tag, b.tag));
158-
159-
currentTagIndex = validTags.findIndex((tag) => tag.tag === currentTag);
160-
currentTagInfo = validTags.at(currentTagIndex);
161-
if (!currentTagInfo) {
162-
throw new Error(`Could not find last stable tag ${currentTag}`);
163-
}
166+
throw new Error(`Could not find tag ${currentTag}`);
164167
}
165168

166169
previousTagInfo = validTags.at(currentTagIndex + 1);
167170
if (!previousTagInfo) {
168-
throw new Error(
169-
`Could not find previous prerelease tag from ${currentTag}`
170-
);
171+
throw new Error(`Could not find previous tag from ${currentTag}`);
171172
}
172173

173174
return {
@@ -232,21 +233,35 @@ interface GitHubGraphqlTag {
232233
interface GitHubGraphqlTagResponse {
233234
repository: {
234235
refs: {
236+
pageInfo: {
237+
hasNextPage: boolean;
238+
endCursor: string;
239+
};
235240
nodes: Array<GitHubGraphqlTag>;
236241
};
237242
};
238243
}
239244

240-
async function getTags(owner: string, repo: string) {
245+
async function getTags(
246+
owner: string,
247+
repo: string,
248+
endCursor?: string,
249+
nodes: Array<GitHubGraphqlTag> = []
250+
): Promise<GitHubGraphqlTag[]> {
241251
let response: GitHubGraphqlTagResponse = await graphqlWithAuth(
242252
gql`
243-
query GET_TAGS($owner: String!, $repo: String!) {
253+
query GET_TAGS($owner: String!, $repo: String!, $endCursor: String) {
244254
repository(owner: $owner, name: $repo) {
245255
refs(
246256
refPrefix: "refs/tags/"
247257
first: 100
248258
orderBy: { field: TAG_COMMIT_DATE, direction: DESC }
259+
after: $endCursor
249260
) {
261+
pageInfo {
262+
hasNextPage
263+
endCursor
264+
}
250265
nodes {
251266
name
252267
target {
@@ -267,15 +282,26 @@ async function getTags(owner: string, repo: string) {
267282
}
268283
}
269284
`,
270-
{ owner, repo }
285+
{ owner, repo, endCursor }
271286
);
272287

273-
return response.repository.refs.nodes.filter((node) => {
288+
let filtered = response.repository.refs.nodes.filter((node) => {
274289
return (
275290
node.name.startsWith(PACKAGE_VERSION_TO_FOLLOW) ||
276291
node.name.startsWith("v0.0.0-nightly-")
277292
);
278293
});
294+
295+
if (response.repository.refs.pageInfo.hasNextPage) {
296+
console.log("has next page", response.repository.refs.pageInfo.endCursor);
297+
298+
return getTags(owner, repo, response.repository.refs.pageInfo.endCursor, [
299+
...nodes,
300+
...filtered,
301+
]);
302+
}
303+
304+
return [...nodes, ...filtered];
279305
}
280306

281307
export async function getIssuesClosedByPullRequests(

scripts/release/utils.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as semver from "semver";
2+
13
import { GITHUB_REPOSITORY, PACKAGE_VERSION_TO_FOLLOW } from "./constants";
24

35
export function checkIfStringStartsWith(
@@ -34,3 +36,7 @@ export function cleanupRef(ref: string) {
3436
export function isNightly(tagName: string) {
3537
return tagName.startsWith("v0.0.0-nightly-");
3638
}
39+
40+
export function isStable(tagName: string) {
41+
return semver.prerelease(tagName) === null;
42+
}

0 commit comments

Comments
 (0)