From 983480131bf6ac48b3834d334deb97a6b3f2f4f6 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 4 Mar 2024 12:54:13 +0900 Subject: [PATCH 1/4] chore: Automated release (#13075) * chore: Automated release * follow --- .github/workflows/release-edit-with-push.yml | 40 ++++++ .github/workflows/release-with-dispatch.yml | 122 +++++++++++++++++++ .github/workflows/release-with-ready.yml | 38 ++++++ 3 files changed, 200 insertions(+) create mode 100644 .github/workflows/release-edit-with-push.yml create mode 100644 .github/workflows/release-with-dispatch.yml create mode 100644 .github/workflows/release-with-ready.yml diff --git a/.github/workflows/release-edit-with-push.yml b/.github/workflows/release-edit-with-push.yml new file mode 100644 index 0000000000..944b98eb7c --- /dev/null +++ b/.github/workflows/release-edit-with-push.yml @@ -0,0 +1,40 @@ +name: "Release Manager: sync changelog with PR" + +on: + push: + branches: + - release/** + paths: + - 'CHANGELOG.md' + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + edit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # headがrelease/かつopenのPRを1つ取得 + - name: Get PR + run: | + echo "pr_number=$(gh pr list --limit 1 --head "${{ github.ref_name }}" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT + id: get_pr + - name: Get target version + uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v1 + id: v + # CHANGELOG.mdの内容を取得 + - name: Get changelog + uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v1 + with: + version: ${{ steps.v.outputs.target_version }} + id: changelog + # PRのnotesを更新 + - name: Update PR + run: | + gh pr edit ${{ steps.get_pr.outputs.pr_number }} --body "${{ steps.changelog.outputs.changelog }}" diff --git a/.github/workflows/release-with-dispatch.yml b/.github/workflows/release-with-dispatch.yml new file mode 100644 index 0000000000..1a954739d9 --- /dev/null +++ b/.github/workflows/release-with-dispatch.yml @@ -0,0 +1,122 @@ +name: "Release Manager [Dispatch]" + +on: + workflow_dispatch: + inputs: + ## Specify the type of the next release. + #version_increment_type: + # type: choice + # description: 'VERSION INCREMENT TYPE' + # default: 'patch' + # required: false + # options: + # - 'major' + # - 'minor' + # - 'patch' + merge: + type: boolean + description: 'MERGE RELEASE BRANCH TO MAIN' + default: false + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + get-pr: + runs-on: ubuntu-latest + outputs: + pr_number: ${{ steps.get_pr.outputs.pr_number }} + steps: + - uses: actions/checkout@v4 + # headがrelease/かつopenのPRを1つ取得 + - name: Get PRs + run: | + echo "pr_number=$(gh pr list --limit 1 --search "head:release/ is:open" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT + id: get_pr + + merge: + uses: misskey-dev/release-manager-actions/.github/workflows/merge.yml@v1 + needs: get-pr + if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge == true }} + with: + pr_number: ${{ needs.get-pr.outputs.pr_number }} + package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }} + # Text to prepend to the changelog + # The first line must be `## Unreleased` + changes_template: | + ## Unreleased + + ### General + - + + ### Client + - + + ### Server + - + + use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }} + secrets: + RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }} + RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }} + RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }} + + create-prerelease: + uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1 + needs: get-pr + if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge != true }} + with: + pr_number: ${{ needs.get-pr.outputs.pr_number }} + package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }} + use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }} + secrets: + RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }} + RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + + create-target: + uses: misskey-dev/release-manager-actions/.github/workflows/create-target.yml@v1 + needs: get-pr + if: ${{ needs.get-pr.outputs.pr_number == '' }} + with: + # The script for version increment. + # process.env.CURRENT_VERSION: The current version. + # + # Misskey calender versioning (yyyy.MM.patch) example + version_increment_script: | + const now = new Date(); + const year = now.toLocaleDateString('en-US', { year: 'numeric', timeZone: 'Asia/Tokyo' }); + const month = now.toLocaleDateString('en-US', { month: 'numeric', timeZone: 'Asia/Tokyo' }); + const [major, minor, _patch] = process.env.CURRENT_VERSION.split('.'); + const patch = Number(_patch.split('-')[0]); + if (Number.isNaN(patch)) { + console.error('Invalid patch version', year, month, process.env.CURRENT_VERSION, major, minor, _patch); + throw new Error('Invalid patch version'); + } + if (year !== major || month !== minor) { + return `${year}.${month}.0`; + } else { + return `${major}.${minor}.${patch + 1}`; + } + ##Semver example + #version_increment_script: | + # const [major, minor, patch] = process.env.CURRENT_VERSION.split('.'); + # if ("${{ inputs.version_increment_type }}" === "major") { + # return `${Number(major) + 1}.0.0`; + # } else if ("${{ inputs.version_increment_type }}" === "minor") { + # return `${major}.${Number(minor) + 1}.0`; + # } else { + # return `${major}.${minor}.${Number(patch) + 1}`; + # } + package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }} + use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }} + secrets: + RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }} + RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }} + RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }} diff --git a/.github/workflows/release-with-ready.yml b/.github/workflows/release-with-ready.yml new file mode 100644 index 0000000000..b64ed20791 --- /dev/null +++ b/.github/workflows/release-with-ready.yml @@ -0,0 +1,38 @@ +name: "Release Manager: release RC when ready for review" + +on: + pull_request: + types: [ready_for_review] + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + check: + runs-on: ubuntu-latest + outputs: + ref: ${{ steps.get_pr.outputs.ref }} + steps: + - uses: actions/checkout@v4 + # PR情報を取得 + - name: Get PR + run: | + pr_json=$(gh pr view ${{ github.event.pull_request.number }} --json isDraft,headRefName) + echo "ref=$(echo $pr_json | jq -r '.headRefName')" >> $GITHUB_OUTPUT + id: get_pr + release: + uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1 + needs: check + if: startsWith(needs.check.outputs.ref, 'release/') + with: + pr_number: ${{ github.event.pull_request.number }} + package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }} + use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }} + secrets: + RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }} + RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} From 9542cb8d6253a93b06a68b9ac3647367f8f7354c Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 4 Mar 2024 13:48:57 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix(backend):=20=E3=83=AA=E3=83=A2=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E3=81=AE=E6=83=85?= =?UTF-8?q?=E5=A0=B1=E3=81=8C=E6=9B=B4=E6=96=B0=E3=81=A7=E3=81=8D=E3=81=AA?= =?UTF-8?q?=E3=81=8F=E3=81=AA=E3=81=A3=E3=81=A6=E3=81=84=E3=81=9F=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13507)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(backend): fetchInstanceMetadataのLockが永遠に解除されない問題を修正 Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com> * fix test * fix * comment * comment * improve test --------- Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com> --- .../src/core/FetchInstanceMetadataService.ts | 28 ++++++++++++++----- .../test/unit/FetchInstanceMetadataService.ts | 24 ++++++++++++++-- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index bc270bd28f..8d173855f3 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -51,21 +51,35 @@ export class FetchInstanceMetadataService { } @bindThis - public async tryLock(host: string): Promise { - const mutex = await this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '1', 'GET'); - return mutex !== '1'; + // public for test + public async tryLock(host: string): Promise { + // TODO: マイグレーションなのであとで消す (2024.3.1) + this.redisClient.del(`fetchInstanceMetadata:mutex:${host}`); + + return await this.redisClient.set( + `fetchInstanceMetadata:mutex:v2:${host}`, '1', + 'EX', 30, // 30秒したら自動でロック解除 https://github.com/misskey-dev/misskey/issues/13506#issuecomment-1975375395 + 'GET' // 古い値を返す(なかったらnull) + ); } @bindThis - public unlock(host: string): Promise<'OK'> { - return this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '0'); + // public for test + public unlock(host: string): Promise { + return this.redisClient.del(`fetchInstanceMetadata:mutex:v2:${host}`); } @bindThis public async fetchInstanceMetadata(instance: MiInstance, force = false): Promise { const host = instance.host; - // Acquire mutex to ensure no parallel runs - if (!await this.tryLock(host)) return; + + // finallyでunlockされてしまうのでtry内でロックチェックをしない + // (returnであってもfinallyは実行される) + if (!force && await this.tryLock(host) === '1') { + // 1が返ってきていたらロックされているという意味なので、何もしない + return; + } + try { if (!force) { const _instance = await this.federatedInstanceService.fetch(host); diff --git a/packages/backend/test/unit/FetchInstanceMetadataService.ts b/packages/backend/test/unit/FetchInstanceMetadataService.ts index 510b84b680..bf8f3ab0e3 100644 --- a/packages/backend/test/unit/FetchInstanceMetadataService.ts +++ b/packages/backend/test/unit/FetchInstanceMetadataService.ts @@ -56,6 +56,7 @@ describe('FetchInstanceMetadataService', () => { } else if (token === DI.redis) { return mockRedis; } + return null; }) .compile(); @@ -78,6 +79,7 @@ describe('FetchInstanceMetadataService', () => { httpRequestService.getJson.mockImplementation(() => { throw Error(); }); const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); + await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any); expect(tryLockSpy).toHaveBeenCalledTimes(1); expect(unlockSpy).toHaveBeenCalledTimes(1); @@ -92,6 +94,7 @@ describe('FetchInstanceMetadataService', () => { httpRequestService.getJson.mockImplementation(() => { throw Error(); }); const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); + await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any); expect(tryLockSpy).toHaveBeenCalledTimes(1); expect(unlockSpy).toHaveBeenCalledTimes(1); @@ -104,13 +107,30 @@ describe('FetchInstanceMetadataService', () => { const now = Date.now(); federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any); httpRequestService.getJson.mockImplementation(() => { throw Error(); }); + await fetchInstanceMetadataService.tryLock('example.com'); const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); - await fetchInstanceMetadataService.tryLock('example.com'); + await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any); - expect(tryLockSpy).toHaveBeenCalledTimes(2); + expect(tryLockSpy).toHaveBeenCalledTimes(1); expect(unlockSpy).toHaveBeenCalledTimes(0); expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0); expect(httpRequestService.getJson).toHaveBeenCalledTimes(0); }); + + test('Do when lock not acquired but forced', async () => { + redisClient.set = mockRedis(); + const now = Date.now(); + federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any); + httpRequestService.getJson.mockImplementation(() => { throw Error(); }); + await fetchInstanceMetadataService.tryLock('example.com'); + const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); + const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); + + await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any, true); + expect(tryLockSpy).toHaveBeenCalledTimes(0); + expect(unlockSpy).toHaveBeenCalledTimes(1); + expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0); + expect(httpRequestService.getJson).toHaveBeenCalled(); + }); }); From 96ab1af03b821ac265a1e8ff492c154b80e02759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:09:24 +0900 Subject: [PATCH 3/4] Update CHANGELOG.md --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bafee277d2..ca7bf85fd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,17 @@ --> +## Unreleased + +### General +- + +### Client +- + +### Server +- + ## 2024.3.1 ### General From 13f5fafdbc869207141f2a2f1f75f61c3147372d Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 4 Mar 2024 10:39:43 +0000 Subject: [PATCH 4/4] remove template txt --- CHANGELOG.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca7bf85fd0..349e99d133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,3 @@ - - ## Unreleased ### General