diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 102313bdd..314d1f150 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -22,10 +22,14 @@ version-resolver: exclude-labels: - "T-Task" - "X-Reverted" + - "backport staging" exclude-contributors: - "RiotRobot" template: | $CHANGES +no-changes-template: "" prerelease: true prerelease-identifier: rc include-pre-releases: false +stable-ref: master +staging-ref: staging diff --git a/.github/workflows/release-drafter-workflow.yml b/.github/workflows/release-drafter-workflow.yml new file mode 100644 index 000000000..798d07791 --- /dev/null +++ b/.github/workflows/release-drafter-workflow.yml @@ -0,0 +1,86 @@ +name: Release Drafter +on: + workflow_call: + inputs: + include-changes: + description: Project to include changelog entries from in this release. + type: string + required: false +concurrency: release-drafter-action +jobs: + draft: + runs-on: ubuntu-latest + steps: + - name: 🧮 Checkout code + uses: actions/checkout@v4 + with: + ref: staging + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version-file: package.json + cache: "yarn" + + - name: Install Deps + run: "yarn install --frozen-lockfile" + + - uses: t3chguy/release-drafter@105e541c2c3d857f032bd522c0764694758fabad + id: draft-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + disable-autolabeler: true + + - name: Get actions scripts + uses: actions/checkout@v4 + with: + repository: matrix-org/matrix-js-sdk + persist-credentials: false + path: .action-repo + sparse-checkout: | + .github/actions + scripts/release + + - name: Ingest upstream changes + if: inputs.include-changes + uses: actions/github-script@v7 + env: + RELEASE_ID: ${{ steps.release.outputs.id }} + DEPENDENCY: ${{ inputs.include-changes }} + VERSION: ${{ steps.draft-release.outputs.tag_name }} + with: + retries: 3 + script: | + const { RELEASE_ID: releaseId, DEPENDENCY, VERSION } = process.env; + const { owner, repo } = context.repo; + const script = require("./.action-repo/scripts/release/merge-release-notes.js"); + + let deps = []; + if (DEPENDENCY.includes("/")) { + deps.push(DEPENDENCY.replace("$VERSION", VERSION)) + } else { + const fromVersion = JSON.parse(await exec.exec("git show origin/master:package.json")).dependencies[DEPENDENCY]; + const toVersion = require("./package.json").dependencies[DEPENDENCY]; + + if (toVersion.endsWith("#develop")) { + core.warning(`${DEPENDENCY} will be kept at ${fromVersion}`, { title: "Develop dependency found" }); + } else { + deps.push([DEPENDENCY, fromVersion, toVersion]); + } + } + + if (deps.length) { + const notes = await script({ + github, + releaseId, + dependencies: deps, + }); + + await github.rest.repos.updateRelease({ + owner, + repo, + release_id: releaseId, + body: notes, + }); + } diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 4d889f456..d8afa80a9 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -2,20 +2,8 @@ name: Release Drafter on: push: branches: [staging] - workflow_dispatch: - inputs: - previous-version: - description: What release to use as a base for release note purposes - required: false - type: string + workflow_dispatch: {} concurrency: ${{ github.workflow }} jobs: draft: - runs-on: ubuntu-latest - steps: - - uses: release-drafter/release-drafter@e64b19c4c46173209ed9f2e5a2f4ca7de89a0e86 # v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - disable-autolabeler: true - previous-version: ${{ inputs.previous-version }} + uses: matrix-org/matrix-js-sdk/.github/workflows/release-drafter-workflow.yml@develop diff --git a/.github/workflows/release-make.yml b/.github/workflows/release-make.yml index b299f9db6..ff66a6c6d 100644 --- a/.github/workflows/release-make.yml +++ b/.github/workflows/release-make.yml @@ -20,10 +20,8 @@ on: description: Publish to npm type: boolean default: false - dependencies: - description: | - List of dependencies to update in `npm-dep=version` format. - `version` can be `"current"` to leave it at the current version. + downstreams: + description: List of github projects (owner/repo) which should have their dependency bumped to the newly released version (in JSON string array string syntax) type: string required: false include-changes: @@ -88,17 +86,11 @@ jobs: id: prepare run: | echo "VERSION=$VERSION" >> $GITHUB_ENV - { - echo "RELEASE_NOTES<> $GITHUB_ENV HAS_DIST=0 jq -e .scripts.dist package.json >/dev/null 2>&1 && HAS_DIST=1 echo "has-dist-script=$HAS_DIST" >> $GITHUB_OUTPUT env: - BODY: ${{ steps.release.outputs.body }} VERSION: ${{ steps.release.outputs.tag_name }} - name: Finalise version @@ -132,76 +124,23 @@ jobs: - name: Install dependencies run: "yarn install --frozen-lockfile" - - name: Update dependencies - id: update-dependencies - if: inputs.dependencies - run: | - UPDATED=() - while IFS= read -r DEPENDENCY; do - [ -z "$DEPENDENCY" ] && continue - IFS="=" read -r PACKAGE UPDATE_VERSION <<< "$DEPENDENCY" - - CURRENT_VERSION=$(cat package.json | jq -r .dependencies[\"$PACKAGE\"]) - echo "Current $PACKAGE version is $CURRENT_VERSION" - - if [ "$CURRENT_VERSION" == "null" ] - then - echo "Unable to find $PACKAGE in package.json" - exit 1 - fi - - if [ "$UPDATE_VERSION" == "current" ] || [ "$UPDATE_VERSION" == "$CURRENT_VERSION" ] - then - echo "Not updating dependency $PACKAGE" - continue - fi - - echo "Upgrading $PACKAGE to $UPDATE_VERSION..." - yarn upgrade "$PACKAGE@$UPDATE_VERSION" --exact - git add -u - git commit -m "Upgrade $PACKAGE to $UPDATE_VERSION" - UPDATED+=("$PACKAGE") - done <<< "$DEPENDENCIES" - - JSON=$(jq --compact-output --null-input '$ARGS.positional' --args -- "${UPDATED[@]}") - echo "updated=$JSON" >> $GITHUB_OUTPUT - env: - DEPENDENCIES: ${{ inputs.dependencies }} - - - name: Prevent develop dependencies - if: inputs.dependencies + - name: Handle develop dependencies run: | ret=0 - cat package.json | jq '.dependencies[]' | grep -q '#develop' || ret=$? - if [ "$ret" -eq 0 ]; then - echo "package.json contains develop dependencies. Refusing to release." - exit - fi + cat package.json | jq -r '.dependencies | to_entries | .[] | "\(.key) \(.value)"' | grep '#develop$' | while read -r dep ; do + IFS=" " + PACKAGE=${dep[0]} + VERSION=${dep[1]} + + echo "::warning title=Develop dependency found::$DEPENDENCY will be kept at $VERSION" + yarn upgrade "$PACKAGE@$VERSION" --exact + git add -u + git commit -m "Keep $PACKAGE at $VERSION" + done - name: Bump package.json version run: yarn version --no-git-tag-version --new-version "${VERSION#v}" - - name: Ingest upstream changes - if: | - inputs.include-changes && - (!inputs.dependencies || contains(fromJSON(steps.update-dependencies.outputs.updated), inputs.include-changes)) - uses: actions/github-script@v7 - env: - RELEASE_ID: ${{ steps.release.outputs.id }} - DEPENDENCY: ${{ inputs.include-changes }} - with: - retries: 3 - script: | - const { RELEASE_ID: releaseId, DEPENDENCY, VERSION } = process.env; - const { owner, repo } = context.repo; - const script = require("./.action-repo/scripts/release/merge-release-notes.js"); - const notes = await script({ - github, - releaseId, - dependencies: [DEPENDENCY.replace("$VERSION", VERSION)], - }); - core.exportVariable("RELEASE_NOTES", notes); - - name: Add to CHANGELOG.md if: inputs.final run: | @@ -219,6 +158,8 @@ jobs: cat CHANGELOG.md.old >> CHANGELOG.md rm CHANGELOG.md.old git add CHANGELOG.md + env: + RELEASE_NOTES: ${{ steps.release.outputs.body }} - name: Run pre-release script to update package.json fields run: | @@ -351,3 +292,31 @@ jobs: filter-labels: X-Upcoming-Release-Blocker remove-labels: X-Upcoming-Release-Blocker add-labels: X-Release-Blocker + + bump-downstreams: + name: Update npm dependency in downstream projects + needs: npm + runs-on: ubuntu-latest + if: inputs.downstreams + strategy: + matrix: + repo: ${{ fromJSON(inputs.downstreams) }} + steps: + - name: Checkout Element Desktop + uses: actions/checkout@v4 + if: inputs.element-desktop + with: + repository: ${{ matrix.repo }} + ref: staging + token: ${{ secrets.ELEMENT_BOT_TOKEN }} + + - name: Bump dependency + env: + DEPENDENCY: ${{ needs.npm.outputs.id }} + run: | + git config --global user.email "releases@riot.im" + git config --global user.name "RiotRobot" + yarn upgrade "$DEPENDENCY" --exact + git add package.json yarn.lock + git commit -am"Upgrade dependency to $DEPENDENCY" + git push origin staging diff --git a/.github/workflows/release-npm.yml b/.github/workflows/release-npm.yml index efba1d967..baca1c404 100644 --- a/.github/workflows/release-npm.yml +++ b/.github/workflows/release-npm.yml @@ -8,6 +8,8 @@ jobs: npm: name: Publish to npm runs-on: ubuntu-latest + outputs: + id: ${{ steps.npm-publish.outputs.id }} steps: - name: 🧮 Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fccff0d61..637585780 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,6 +28,7 @@ jobs: with: final: ${{ inputs.mode == 'final' }} npm: ${{ inputs.npm }} + downstreams: '["matrix-org/matrix-react-sdk", "element-hq/element-web"]' docs: name: Publish Documentation diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 8a1d7686e..e19198e62 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -4,6 +4,5 @@ # Deep dive -- [Release Process](release.md) - [Storage notes](storage-notes.md) - [Unverified devices](warning-on-unverified-devices.md) diff --git a/docs/release.md b/docs/release.md deleted file mode 100644 index 7520abdc6..000000000 --- a/docs/release.md +++ /dev/null @@ -1,24 +0,0 @@ -# Release Process - -## Hotfix and off-cycle releases - -1. Prepare the `staging` branch by using the backport automation and manually merging -2. Go to [Releasing](#Releasing) - -## Release candidates - -1. Prepare the `staging` branch by running the [branch cut automation](https://github.com/vector-im/element-web/actions/workflows/release_prepare.yml) -2. Go to [Releasing](#Releasing) - -## Releasing - -1. Open the [Releases page](https://github.com/matrix-org/matrix-js-sdk/releases) and inspect the draft release there -2. Make any modifications to the release notes and tag/version as required -3. Run [workflow](https://github.com/matrix-org/matrix-js-sdk/actions/workflows/release.yml) with the type set appropriately - -## Artifacts - -Releasing the Matrix JS SDK has just two artifacts: - -- Package published to [npm](https://github.com/matrix-org/matrix-js-sdk) -- Docs published to [Github Pages](https://matrix-org.github.io/matrix-js-sdk/) diff --git a/package.json b/package.json index 0ab62af76..3cd5c875e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-js-sdk", - "version": "31.2.0", + "version": "31.3.0-rc.0", "description": "Matrix Client-Server SDK for Javascript", "engines": { "node": ">=18.0.0" @@ -93,7 +93,7 @@ "@types/uuid": "9", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", - "allchange": "^1.0.6", + "allchange": "^1.3.0", "babel-jest": "^29.0.0", "debug": "^4.3.4", "domexception": "^4.0.0", diff --git a/scripts/release/merge-release-notes.js b/scripts/release/merge-release-notes.js index 7b5e9f8c4..1a0db0c81 100755 --- a/scripts/release/merge-release-notes.js +++ b/scripts/release/merge-release-notes.js @@ -2,6 +2,31 @@ const fs = require("fs"); +// Dependency can be the name of an entry in package.json, in which case the owner, repo & version will be looked up in its own package.json +// Or it can be a string in the form owner/repo@tag +// Or it can be a tuple of dependency, from version, to version, in which case a list of releases in that range (to inclusive) will be returned +async function getReleases(github, dependency) { + if (Array.isArray(dependency)) { + const [dep, fromVersion, toVersion] = dependency; + const upstreamPackageJson = getDependencyPackageJson(dep); + const [owner, repo] = upstreamPackageJson.repository.url.split("/").slice(-2); + + const response = await github.rest.repos.listReleases({ + owner, + repo, + per_page: 100, + }); + const releases = response.data.filter((release) => !release.draft && !release.prerelease); + + const fromVersionIndex = releases.findIndex((release) => release.tag_name === `v${fromVersion}`); + const toVersionIndex = releases.findIndex((release) => release.tag_name === `v${toVersion}`); + + return releases.slice(toVersionIndex, fromVersionIndex); + } + + return [await getRelease(github, dependency)]; +} + async function getRelease(github, dependency) { let owner; let repo; @@ -11,7 +36,7 @@ async function getRelease(github, dependency) { repo = dependency.split("/")[1].split("@")[0]; tag = dependency.split("@")[1]; } else { - const upstreamPackageJson = JSON.parse(fs.readFileSync(`./node_modules/${dependency}/package.json`, "utf8")); + const upstreamPackageJson = getDependencyPackageJson(dependency); [owner, repo] = upstreamPackageJson.repository.url.split("/").slice(-2); tag = `v${upstreamPackageJson.version}`; } @@ -24,6 +49,10 @@ async function getRelease(github, dependency) { return response.data; } +function getDependencyPackageJson(dependency) { + return JSON.parse(fs.readFileSync(`./node_modules/${dependency}/package.json`, "utf8")); +} + const HEADING_PREFIX = "## "; const categories = [ @@ -56,8 +85,10 @@ const main = async ({ github, releaseId, dependencies }) => { const sections = Object.fromEntries(categories.map((cat) => [cat, []])); for (const dependency of dependencies) { - const release = await getRelease(github, dependency); - parseReleaseNotes(release.body, sections); + const releases = await getReleases(github, dependency); + for (const release of releases) { + parseReleaseNotes(release.body, sections); + } } const { data: release } = await github.rest.repos.getRelease({ diff --git a/yarn.lock b/yarn.lock index dabeb0df0..12eb0946d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2275,10 +2275,10 @@ ajv@^6.12.4, ajv@~6.12.6: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -allchange@^1.0.6: - version "1.1.0" - resolved "https://registry.yarnpkg.com/allchange/-/allchange-1.1.0.tgz#f8fa129e4b40c0b0a2c072c530f2324c6590e208" - integrity sha512-brDWf2feuL3FRyivSyC6AKOgpX+bYgs1Z7+ZmLti6PnBdZgIjRSnKvlc68N8+1UX2rCISx2I+XuUvE3/GJNG2A== +allchange@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/allchange/-/allchange-1.3.0.tgz#0d38e76e069eacd0279fb9a171770426d8e57d37" + integrity sha512-orTQYJQzY98ZNvh9VFpBpxLry9obXvDOYuQZXDnTL/YJL3sphgr93norJrR8Qz8mNlJ3yEm1YS+aEEbC3/3Wjg== dependencies: "@actions/core" "^1.4.0" "@actions/github" "^5.0.0" @@ -2287,7 +2287,7 @@ allchange@^1.0.6: js-yaml "^4.1.0" loglevel "^1.7.1" semver "^7.3.5" - yargs "^17.0.1" + yargs "^17.5.1" another-json@^0.2.0: version "0.2.0" @@ -6719,7 +6719,7 @@ yargs-parser@^21.1.1: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@^17.0.1, yargs@^17.3.1, yargs@^17.5.1: +yargs@^17.3.1, yargs@^17.5.1: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==