You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by "YayoiNanoka (via GitHub)" <gi...@apache.org> on 2026/09/01 03:07:31 UTC
Re: [PR] ci: manage inactive issues and pull requests [maka]
YayoiNanoka commented on code in PR #4391:
URL: https://github.com/apache/maka/pull/4391#discussion_r3900425819
##########
.github/workflows/issue-pr-lifecycle.yml:
##########
@@ -0,0 +1,315 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: Issue and PR lifecycle
+
+on:
+ schedule:
+ - cron: "41 3 * * *"
+ workflow_dispatch:
+ inputs:
+ dry_run:
+ description: Report planned lifecycle changes without writing to GitHub.
+ required: false
+ default: true
+ type: boolean
+ issues:
+ types: [reopened]
+ pull_request:
Review Comment:
**[P2] Handle reopened fork PRs in a trusted writable context**
This trigger performs writes (`removeStale` and `createComment`), but GitHub normally downscopes the `pull_request` event's `GITHUB_TOKEN` to read-only for a public fork, even though this workflow requests `issues: write` and `pull-requests: write`. Reopening a stale fork PR will therefore fail before clearing its old lifecycle state; if the previous grace period has already expired, the next scheduled sweep can close it again.
Please move the reopened-PR mutation to a narrowly scoped trusted event such as `pull_request_target`, and import only trusted base/default-branch code. The existing `pr-effort-label.yml` workflow demonstrates the appropriate pattern. In particular, do not combine a writable token with checkout or execution of the untrusted PR head. GitHub documents the fork-token restriction here: https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#changing-the-permissions-in-a-forked-repository
<details>
<summary>中文</summary>
**[P2] 请在可信、可写的上下文中处理外部 fork PR 的 reopen**
这个触发器会执行写操作(`removeStale` 和 `createComment`),但对于公开 fork,GitHub 通常会把 `pull_request` 事件的 `GITHUB_TOKEN` 降为只读,即使 workflow 声明了 `issues: write` 和 `pull-requests: write`。因此,重新打开带 stale 状态的 fork PR 时,workflow 会在清理旧生命周期状态之前失败;如果之前的宽限期已经结束,下一次定时扫描可能再次关闭它。
请把 reopened PR 的状态变更移到范围严格受限的可信事件中,例如 `pull_request_target`,并且只导入 base/default branch 上的可信代码。现有的 `pr-effort-label.yml` 已经展示了合适的安全模式。尤其不要同时使用可写 token 和不可信 PR head 的 checkout 或代码执行。GitHub 对 fork token 的限制见上面的官方文档。
</details>
##########
scripts/issue-pr-lifecycle.mjs:
##########
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const DAY_MS = 24 * 60 * 60 * 1000;
+
+export const STALE_WARNING_MARKER = '<!-- maka-lifecycle:stale-warning -->';
+export const STALE_CLOSE_MARKER = '<!-- maka-lifecycle:stale-close -->';
+export const STALE_REOPEN_MARKER = '<!-- maka-lifecycle:reopened -->';
+
+export const LIFECYCLE_LABELS = [
+ {
+ name: 'stale',
+ color: 'ededed',
+ description: 'No qualifying activity within the lifecycle policy window',
+ },
+ {
+ name: 'pinned',
+ color: '0e8a16',
+ description: 'Exempt from automated lifecycle management',
+ },
+];
+
+const POLICY = {
+ issue: { inactiveDays: 30, graceDays: 30 },
+ pull_request: { inactiveDays: 30, graceDays: 7 },
+};
+
+const ISSUE_WARNING = `${STALE_WARNING_MARKER}
+This issue has had no human activity for 30 days and has been marked stale. It will be closed in 30 days unless someone comments.
+
+If the issue is still current, please confirm it against the latest \`main\` and add any information that would help move it forward. Assigned issues and issues labelled \`pinned\` are exempt from this policy.`;
+
+const ISSUE_CLOSE = `${STALE_CLOSE_MARKER}
+This issue has been closed because it received no human response during the 30-day grace period. This is not a judgement on the value of the report.
+
+If the issue still applies to the latest \`main\`, please comment with updated evidence so a maintainer can reopen it, or open a follow-up issue with the missing information.`;
+
+const PR_WARNING = `${STALE_WARNING_MARKER}
+This pull request has had no new commits for 30 days and has been marked stale. It will be closed in 7 days unless a new commit is pushed.
+
+Comments do not reset this timer: only a new commit does. If the pull request is intentionally long-lived, a maintainer can apply the \`pinned\` label.`;
+
+const PR_CLOSE = `${STALE_CLOSE_MARKER}
+This pull request has been closed because it received no new commits during the 7-day grace period. This is not a judgement on the merit of the change; it only keeps the review queue aligned with work that is actively in flight.
+
+The branch and review history are preserved. Push a new commit and reopen the pull request whenever the work is ready to continue.`;
+
+function time(value, field) {
+ const result = Date.parse(value);
+ if (!Number.isFinite(result)) throw new TypeError(`${field} must be an ISO timestamp`);
+ return result;
+}
+
+function isBot(comment) {
+ return (
+ comment.author?.__typename === 'Bot' ||
+ String(comment.author?.login ?? '') === 'github-actions[bot]'
+ );
+}
+
+function latestTimestamp(values, fallback, field) {
+ return values.reduce((latest, value) => Math.max(latest, time(value, field)), fallback);
+}
+
+function issueActivityAt(item) {
+ const createdAt = time(item.createdAt, 'createdAt');
+ const qualifyingComments = (item.comments ?? [])
+ .filter(
+ (comment) => !isBot(comment) || String(comment.body ?? '').includes(STALE_REOPEN_MARKER),
+ )
+ .map((comment) => comment.createdAt);
+ return latestTimestamp(qualifyingComments, createdAt, 'comment.createdAt');
+}
+
+function pullRequestActivityAt(item) {
+ const lastCommitAt = time(item.lastCommitAt ?? item.createdAt, 'lastCommitAt');
+ const reopenAt = latestTimestamp(
+ (item.comments ?? [])
+ .filter(
+ (comment) => isBot(comment) && String(comment.body ?? '').includes(STALE_REOPEN_MARKER),
+ )
+ .map((comment) => comment.createdAt),
+ Number.NEGATIVE_INFINITY,
+ 'comment.createdAt',
+ );
+ return Math.max(lastCommitAt, reopenAt);
Review Comment:
**[P2] Keep the PR activity clock commit-only**
Discussion #3839 and `PR_WARNING` both define PR activity as the latest commit: comments do not reset the timer. Taking the maximum with `reopenAt` changes that contract. A PR with a months-old commit can be reopened without a new commit and receive another 30-day active period. The focused reproduction returns `unstale` for the stale state and then `none: recent qualifying activity` after the label is removed.
Please keep `pullRequestActivityAt()` based exclusively on `lastCommitAt`. Reopening may clear `stale` and begin a fresh warning/grace cycle if that is the desired recovery behavior, but it should not make an old commit recent. The Issue reopen marker can remain separate if reopening an Issue is intended to restart its clock.
<details>
<summary>中文</summary>
**[P2] 保持 PR 活动时钟仅由 commit 决定**
Discussion #3839 和 `PR_WARNING` 都把 PR 活动定义为最后一次 commit:评论不会重置计时。这里再与 `reopenAt` 取最大值改变了这个契约。一个最后 commit 已经过了几个月的 PR,可以在没有新 commit 的情况下通过 reopen 重新获得 30 天活跃期。最小复现中,带 stale 状态时 planner 返回 `unstale`,移除标签后又返回 `none: recent qualifying activity`。
请让 `pullRequestActivityAt()` 只以 `lastCommitAt` 为依据。如果希望 reopen 能够恢复流程,可以移除 `stale` 并重新开始提醒/宽限期,但不应把旧 commit 变成最近活动。如果 Issue 的 reopen 确实应该重置计时,可以单独保留 Issue 的 reopen marker。
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org