diff --git a/.github/actions/lock_discussion/action.yml b/.github/actions/lock_discussion/action.yml new file mode 100644 index 0000000..fbb58ed --- /dev/null +++ b/.github/actions/lock_discussion/action.yml @@ -0,0 +1,12 @@ +name: "Lock discussions" +description: "Locks answered discussions" + +runs: + using: "composite" + steps: + - name: Use Node.js 18 + uses: actions/setup-node@v3 + with: + node-version: 18 + - run: TOKEN=${{inputs.token}} REPO=${{inputs.repo}} node ${{ github.action_path }}/index.js + shell: bash diff --git a/.github/actions/lock_discussion/index.mjs b/.github/actions/lock_discussion/index.mjs new file mode 100644 index 0000000..c698fc8 --- /dev/null +++ b/.github/actions/lock_discussion/index.mjs @@ -0,0 +1,98 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import fetch from "node-fetch"; +import { graphql } from "@octokit/graphql" +global.fetch = fetch; + +const graphqlWithAuth = graphql.defaults({ + headers: { + authorization: process.env.TOKEN, + }, +}); + +async function lockDiscussions(ids) { + for (let discussionId of ids) { + try { + await graphqlWithAuth( + ` + mutation lock ($id: ID!) { + lockLockable(input:{lockableId: $id}) { + clientMutationId + } + } + `, + { + id: discussionId, + } + ); + } catch (e) { + console.log(e); + } + } +} + +async function main () { + const unlockedNodes = [] + const oneDay = 1000 * 60 * 60 * 24; // one day in ms + const today = new Date(); + let readDiscussions = true + let cursor = null + while (readDiscussions) { + const {repository} = await graphqlWithAuth(` + query testQuery ($owner: String!, $repo: String!, $lastId: String) { + repository(owner:$owner, name:$repo) { + discussions(states:[OPEN], first:4, after: $lastId) { + totalCount + nodes { + id + answerChosenAt + createdAt + updatedAt + locked + title + category { + name + } + } + pageInfo { + endCursor + hasNextPage + } + } + } + } + `, { + owner: "cloudscape-design", + repo: process.env.REPO, + lastId: cursor, + }); + + repository.discussions.nodes.forEach(discussion => { + if (!discussion.locked) { + const isQA = discussion.category.name === 'Q&A' + const lockQA = !!discussion.answerChosenAt + && (today - new Date(discussion.answerChosenAt).getTime()) / oneDay > 7 + + const lockGeneral = !!discussion.updatedAt + && discussion.updatedAt !== discussion.createdAt + && (today - new Date(discussion.updatedAt).getTime()) / oneDay > 7 + + const shouldBeLocked = isQA ? lockQA : lockGeneral; + if (shouldBeLocked) { + unlockedNodes.push(discussion.id) + } + } + }) + const {endCursor, hasNextPage} = repository.discussions.pageInfo; + readDiscussions = hasNextPage + cursor = endCursor + } + + console.log(`Discussions to lock: ${unlockedNodes.length}`); + if (unlockedNodes.length) { + await lockDiscussions(unlockedNodes); + } + console.log('Locking completed'); +} + +main(); diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 0000000..2aadb12 --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,21 @@ +name: Lock github threads + +on: + workflow_call: + inputs: + repository: + type: string + description: "Name of repository in organization" + required: true + +permissions: + discussions: write + +jobs: + discussion: + runs-on: ubuntu-latest + steps: + - uses: cloudscape-design/.github/.github/actions/lock_discussions@main + with: + token: ${{ secrets.GITHUB_TOKEN }} + repo: ${{inputs.repository}}