Skip to content

Commit

Permalink
ci(release): sync with remix (#9813)
Browse files Browse the repository at this point in the history
* 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)
  • Loading branch information
mcansh committed Jan 11, 2023
1 parent de3c96a commit 9640d01
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 87 deletions.
17 changes: 0 additions & 17 deletions .github/workflows/postrelease.yml

This file was deleted.

71 changes: 50 additions & 21 deletions .github/workflows/release.yml
@@ -1,39 +1,39 @@
name: 🕊 Release
name: 🦋 Changesets Release
on:
push:
branches:
- release
- "release-*"
- "!release-experimental"
- "!release-experimental-*"

concurrency: ${{ github.workflow }}-${{ github.ref }}

env:
CI: true
- "!release-manual"
- "!release-manual-*"

jobs:
release:
name: 🦋 Changesets Release
if: github.repository == 'remix-run/react-router'
runs-on: ubuntu-latest

outputs:
publishedPackages: ${{ steps.changesets.outputs.publishedPackages }}
published: ${{ steps.changesets.outputs.published }}
steps:
- name: 🛑 Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.11.0

- name: ⬇️ Checkout repo
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: ⎔ Setup Node
- name: ⎔ Setup node
uses: actions/setup-node@v3
with:
node-version-file: ".nvmrc"
cache: yarn
cache: "yarn"

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

- name: 🔐 Setup npm auth
run: |
Expand All @@ -52,16 +52,45 @@ jobs:
version: yarn run version
commit: "chore: Update version for release"
title: "chore: Update version for release"
publish: yarn release
publish: yarn run release
createGithubReleases: false
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN_SO_OTHER_ACTIONS_RUN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

# comment:
# needs: [release]
# name: 📝 Comment on related issues and pull requests
# if: github.repository == 'remix-run/react-router'
# uses: remix-run/react-router/.github/workflows/release-comments.yml@main
# with:
# ref: ${{ github.ref }}
findPackage:
name: 🦋 Find Package
needs: [release]
runs-on: ubuntu-latest
if: github.repository == 'remix-run/react-router' && needs.release.outputs.published == 'true'
outputs:
package: ${{ steps.findPackage.outputs.package }}
steps:
- name: 🛑 Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.11.0

- name: ⬇️ Checkout repo
uses: actions/checkout@v3

- name: ⎔ Setup node
uses: actions/setup-node@v3
with:
node-version: 16
cache: "npm"

- id: findPackage
run: |
package=$(node ./scripts/release/find-release-from-changeset.js)
echo "package=${package}" >> $GITHUB_OUTPUT
env:
packageVersionToFollow: "react-router"
publishedPackages: ${{ needs.release.outputs.publishedPackages }}

comment:
name: 📝 Comment on related issues and pull requests
if: github.repository == 'remix-run/react-router' && needs.findPackage.outputs.package != ''
needs: [release, findPackage]
uses: ./.github/workflows/release-comments.yml
with:
ref: refs/tags/${{ needs.findPackage.outputs.package }}
packageVersionToFollow: "react-router"
41 changes: 15 additions & 26 deletions scripts/release/comment.ts
Expand Up @@ -3,7 +3,7 @@ import {
OWNER,
REPO,
PR_FILES_STARTS_WITH,
IS_NIGHTLY_RELEASE,
IS_STABLE_RELEASE,
AWAITING_RELEASE_LABEL,
} from "./constants";
import {
Expand Down Expand Up @@ -53,7 +53,7 @@ async function commentOnIssuesAndPrsAboutRelease() {
let prLabels = pr.labels.map((label) => label.name);
let prIsAwaitingRelease = prLabels.includes(AWAITING_RELEASE_LABEL);

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

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

if (IS_NIGHTLY_RELEASE || !prIsAwaitingRelease) {
console.log(`commenting on ${issueUrl}`);
promises.push(
commentOnIssue({
owner: OWNER,
repo: REPO,
issue: issue.number,
version: VERSION,
})
);
} else {
console.log(`commenting on and closing ${issueUrl}`);
promises.push(
commentOnIssue({
owner: OWNER,
repo: REPO,
issue: issue.number,
version: VERSION,
})
);
promises.push(
commentOnIssue({
owner: OWNER,
repo: REPO,
issue: issue.number,
version: VERSION,
})
);

if (IS_STABLE_RELEASE) {
console.log(`closing issue ${issueUrl}`);
promises.push(
closeIssue({ owner: OWNER, repo: REPO, issue: issue.number })
);
Expand All @@ -104,10 +96,7 @@ async function commentOnIssuesAndPrsAboutRelease() {
let result = await Promise.allSettled(promises);
let rejected = result.filter((r) => r.status === "rejected");
if (rejected.length > 0) {
console.log(
"🚨 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"
);
console.log(rejected);
console.error("🚨 failed to comment on some issues/prs", rejected);
}
}

Expand Down
3 changes: 2 additions & 1 deletion scripts/release/constants.ts
@@ -1,4 +1,4 @@
import { cleanupRef, cleanupTagName, isNightly } from "./utils";
import { cleanupRef, cleanupTagName, isNightly, isStable } from "./utils";

if (!process.env.DEFAULT_BRANCH) {
throw new Error("DEFAULT_BRANCH is required");
Expand Down Expand Up @@ -32,3 +32,4 @@ export const NIGHTLY_BRANCH = process.env.NIGHTLY_BRANCH;
export const PR_FILES_STARTS_WITH = ["packages/"];
export const IS_NIGHTLY_RELEASE = isNightly(VERSION);
export const AWAITING_RELEASE_LABEL = "awaiting release";
export const IS_STABLE_RELEASE = isStable(VERSION);
38 changes: 38 additions & 0 deletions scripts/release/find-release-from-changeset.js
@@ -0,0 +1,38 @@
/**
*
* @param {string | undefined} publishedPackages
* @param {string | undefined} packageVersionToFollow
* @returns {string | undefined}
*/
function findReleaseFromChangeset(publishedPackages, packageVersionToFollow) {
if (!publishedPackages) {
throw new Error("No published packages found");
}

let packages = JSON.parse(publishedPackages);

if (!Array.isArray(packages)) {
throw new Error("Published packages is not an array");
}

/** @see https://github.com/changesets/action#outputs */
/** @type { { name: string; version: string }[] } */
let typed = packages.filter((pkg) => "name" in pkg && "version" in pkg);

let found = typed.find((pkg) => pkg.name === packageVersionToFollow);

if (!found) {
throw new Error(
`${packageVersionToFollow} was not found in the published packages`
);
}

let result = `${found.name}@${found.version}`;
console.log(result);
return result;
}

findReleaseFromChangeset(
process.env.publishedPackages,
process.env.packageVersionToFollow
);
70 changes: 48 additions & 22 deletions scripts/release/github.ts
Expand Up @@ -7,9 +7,12 @@ import {
DEFAULT_BRANCH,
PACKAGE_VERSION_TO_FOLLOW,
AWAITING_RELEASE_LABEL,
IS_NIGHTLY_RELEASE,
IS_STABLE_RELEASE,
} from "./constants";
import { gql, graphqlWithAuth, octokit } from "./octokit";
import type { MinimalTag } from "./utils";
import { isNightly, isStable } from "./utils";
import { cleanupTagName } from "./utils";
import { checkIfStringStartsWith } from "./utils";

Expand Down Expand Up @@ -140,34 +143,32 @@ function getPreviousTagFromCurrentTag(

return { tag: tagName, date, isPrerelease };
})
.filter((v: any): v is MinimalTag => typeof v !== "undefined");
.filter((v: any): v is MinimalTag => typeof v !== "undefined")
.filter((tag) => {
if (IS_STABLE_RELEASE) return isStable(tag.tag);
let isNightlyTag = isNightly(tag.tag);
if (IS_NIGHTLY_RELEASE) return isNightlyTag;
return !isNightlyTag;
})
.sort((a, b) => {
if (IS_NIGHTLY_RELEASE) {
return b.date.getTime() - a.date.getTime();
}

return semver.rcompare(a.tag, b.tag);
});

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

if (!currentTagInfo) {
throw new Error(`Could not find last tag ${currentTag}`);
}

// if the currentTag was a stable tag, then we want to find the previous stable tag
if (!currentTagInfo.isPrerelease) {
validTags = validTags
.filter((tag) => !tag.isPrerelease)
.sort((a, b) => semver.rcompare(a.tag, b.tag));

currentTagIndex = validTags.findIndex((tag) => tag.tag === currentTag);
currentTagInfo = validTags.at(currentTagIndex);
if (!currentTagInfo) {
throw new Error(`Could not find last stable tag ${currentTag}`);
}
throw new Error(`Could not find tag ${currentTag}`);
}

previousTagInfo = validTags.at(currentTagIndex + 1);
if (!previousTagInfo) {
throw new Error(
`Could not find previous prerelease tag from ${currentTag}`
);
throw new Error(`Could not find previous tag from ${currentTag}`);
}

return {
Expand Down Expand Up @@ -232,21 +233,35 @@ interface GitHubGraphqlTag {
interface GitHubGraphqlTagResponse {
repository: {
refs: {
pageInfo: {
hasNextPage: boolean;
endCursor: string;
};
nodes: Array<GitHubGraphqlTag>;
};
};
}

async function getTags(owner: string, repo: string) {
async function getTags(
owner: string,
repo: string,
endCursor?: string,
nodes: Array<GitHubGraphqlTag> = []
): Promise<GitHubGraphqlTag[]> {
let response: GitHubGraphqlTagResponse = await graphqlWithAuth(
gql`
query GET_TAGS($owner: String!, $repo: String!) {
query GET_TAGS($owner: String!, $repo: String!, $endCursor: String) {
repository(owner: $owner, name: $repo) {
refs(
refPrefix: "refs/tags/"
first: 100
orderBy: { field: TAG_COMMIT_DATE, direction: DESC }
after: $endCursor
) {
pageInfo {
hasNextPage
endCursor
}
nodes {
name
target {
Expand All @@ -267,15 +282,26 @@ async function getTags(owner: string, repo: string) {
}
}
`,
{ owner, repo }
{ owner, repo, endCursor }
);

return response.repository.refs.nodes.filter((node) => {
let filtered = response.repository.refs.nodes.filter((node) => {
return (
node.name.startsWith(PACKAGE_VERSION_TO_FOLLOW) ||
node.name.startsWith("v0.0.0-nightly-")
);
});

if (response.repository.refs.pageInfo.hasNextPage) {
console.log("has next page", response.repository.refs.pageInfo.endCursor);

return getTags(owner, repo, response.repository.refs.pageInfo.endCursor, [
...nodes,
...filtered,
]);
}

return [...nodes, ...filtered];
}

export async function getIssuesClosedByPullRequests(
Expand Down
6 changes: 6 additions & 0 deletions scripts/release/utils.ts
@@ -1,3 +1,5 @@
import * as semver from "semver";

import { GITHUB_REPOSITORY, PACKAGE_VERSION_TO_FOLLOW } from "./constants";

export function checkIfStringStartsWith(
Expand Down Expand Up @@ -34,3 +36,7 @@ export function cleanupRef(ref: string) {
export function isNightly(tagName: string) {
return tagName.startsWith("v0.0.0-nightly-");
}

export function isStable(tagName: string) {
return semver.prerelease(tagName) === null;
}

0 comments on commit 9640d01

Please sign in to comment.