Skip to content

Commit d67ced7

Browse files
authored
Merge pull request #46296 from github/repo-sync
Repo sync
2 parents f71bee7 + 3179338 commit d67ced7

38 files changed

Lines changed: 568 additions & 54 deletions

File tree

‎.github/CONTRIBUTING.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Use the table of contents icon <img alt="Table of contents icon" src="/contribut
1414

1515
**Start here:** Visit [docs.github.com/en/contributing](https://docs.github.com/en/contributing) for complete contributor onboarding and guidelines.
1616

17+
As noted in our contributor onboarding guidelines, if an issue already exists, please don't start work on it until it has been assigned a `help wanted` label.
18+
1719
For repository-specific setup, read the [README](../README.md) file. The official docs site also provides these helpful resources:
1820

1921
- [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
name: Close PRs for issues without help wanted
2+
3+
# docs-internal mirrors workflows to github/docs, so enforce this rule only in the public repo.
4+
on:
5+
pull_request_target:
6+
types: [opened, edited, reopened, synchronize]
7+
8+
permissions:
9+
contents: read
10+
issues: write
11+
pull-requests: write
12+
13+
jobs:
14+
close-without-help-wanted-issue:
15+
if: >-
16+
${{
17+
github.repository == 'github/docs' &&
18+
github.event.pull_request.state == 'open'
19+
}}
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: Close the pull request if a linked issue lacks the help wanted label
23+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
24+
with:
25+
script: |
26+
const { owner, repo } = context.repo
27+
const pull_number = context.payload.pull_request.number
28+
29+
const { repository } = await github.graphql(
30+
`query($owner: String!, $repo: String!, $number: Int!) {
31+
repository(owner: $owner, name: $repo) {
32+
pullRequest(number: $number) {
33+
state
34+
closingIssuesReferences(first: 100) {
35+
nodes {
36+
labels(first: 100) {
37+
nodes {
38+
name
39+
}
40+
}
41+
}
42+
}
43+
}
44+
}
45+
}`,
46+
{ owner, repo, number: pull_number },
47+
)
48+
49+
const pullRequest = repository?.pullRequest
50+
if (!pullRequest) {
51+
throw new Error(`Pull request #${pull_number} was not found`)
52+
}
53+
if (pullRequest.state !== 'OPEN') {
54+
console.log(`Pull request #${pull_number} is no longer open`)
55+
return
56+
}
57+
58+
const closingIssues = pullRequest.closingIssuesReferences.nodes
59+
if (!closingIssues.length) {
60+
console.log(`Pull request #${pull_number} has no closing issue references`)
61+
return
62+
}
63+
64+
const issuesWithoutHelpWanted = closingIssues.filter(issue =>
65+
!issue.labels.nodes.some(label => label.name.toLowerCase() === 'help wanted'),
66+
)
67+
if (!issuesWithoutHelpWanted.length) {
68+
console.log('Every linked issue has the help wanted label')
69+
return
70+
}
71+
72+
await github.rest.issues.createComment({
73+
owner,
74+
repo,
75+
issue_number: pull_number,
76+
body: "Please don't raise pull requests against an existing issue that another user has raised until it's been marked with a `help wanted` label. We need to properly assess all issues before we can accept community contributions, and determine whether it's something we need to fix internally, or how many articles are affected and what the scope of the fix will be. For now, we're going to close this pull request.",
77+
})
78+
79+
await github.rest.pulls.update({
80+
owner,
81+
repo,
82+
pull_number,
83+
state: 'closed',
84+
})

‎.github/workflows/confirm-internal-staff-work-in-docs.yml‎

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,123 @@ jobs:
6565
6666
core.setOutput('did_warn', 'true')
6767
68-
- name: Send Slack notification if a GitHub employee who isn't on the Technical Content team opens an issue in public
69-
if: ${{ steps.membership_check.outputs.did_warn && github.repository == 'github/docs' }}
68+
- name: Resolve Slack user ID from username
69+
id: slack_user
70+
if: ${{ steps.membership_check.outputs.did_warn == 'true' }}
71+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
72+
env:
73+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_DOCS_BOT_TOKEN }}
74+
with:
75+
github-token: ${{ github.token }}
76+
script: |
77+
const githubLogin = context.payload.sender.login.toLowerCase();
78+
79+
core.info(`Looking up Slack user for @${githubLogin}...`);
80+
core.setOutput('slack_user_id', '');
81+
core.setOutput('slack_mention', `GitHub user \`${context.payload.sender.login}\``);
82+
83+
const token = process.env.SLACK_BOT_TOKEN;
84+
if (!token) {
85+
throw new Error('SLACK_DOCS_BOT_TOKEN is not configured.');
86+
}
87+
88+
let cursor = '';
89+
let pages = 0;
90+
const matches = [];
91+
92+
do {
93+
core.info(`Fetching Slack users.list page ${pages + 1}...`);
94+
const url = new URL('https://slack.com/api/users.list');
95+
url.searchParams.set('limit', '200');
96+
if (cursor) {
97+
url.searchParams.set('cursor', cursor);
98+
}
99+
100+
const response = await fetch(url, {
101+
headers: {
102+
Authorization: `Bearer ${token}`,
103+
},
104+
});
105+
106+
if (!response.ok) {
107+
throw new Error(`Slack users.list returned HTTP ${response.status}.`);
108+
}
109+
110+
const result = await response.json();
111+
if (!result.ok) {
112+
throw new Error(`Slack users.list failed: ${result.error || 'unknown error'}.`);
113+
}
114+
115+
if (!Array.isArray(result.members)) {
116+
throw new Error('Slack users.list response did not include a members array.');
117+
}
118+
119+
pages += 1;
120+
matches.push(...result.members.filter((member) =>
121+
!member.deleted &&
122+
!member.is_bot &&
123+
!member.is_app_user &&
124+
typeof member.id === 'string' &&
125+
typeof member.name === 'string' &&
126+
member.name.toLowerCase() === githubLogin
127+
));
128+
129+
cursor = result.response_metadata?.next_cursor || '';
130+
} while (cursor);
131+
132+
core.info(`Checked ${pages} pages of users.list. Found ${matches.length} match(es) for @${githubLogin}.`);
133+
if (matches.length !== 1) {
134+
core.warning(`Expected one Slack username matching @${githubLogin}; found ${matches.length}. No DM will be sent.`);
135+
return;
136+
}
137+
138+
core.info(`Will DM Slack user ${matches[0].id}.`);
139+
core.setOutput('slack_user_id', matches[0].id);
140+
core.setOutput('slack_mention', `<@${matches[0].id}>`);
141+
142+
- name: DM the author in Slack
143+
id: slack_dm
144+
if: ${{ steps.slack_user.outputs.slack_user_id != '' }}
145+
uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0
146+
with:
147+
method: chat.postMessage
148+
token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }}
149+
errors: true
150+
payload: |
151+
channel: ${{ steps.slack_user.outputs.slack_user_id }}
152+
text: ${{ toJSON(format('You opened {0} publicly in github/docs instead of the private repo. An internal confirmation issue was created.', github.event.issue.html_url || github.event.pull_request.html_url)) }}
153+
154+
- name: Log Slack DM result
155+
if: ${{ !cancelled() && steps.membership_check.outputs.did_warn == 'true' }}
156+
env:
157+
SLACK_USER_ID: ${{ steps.slack_user.outputs.slack_user_id }}
158+
SLACK_LOOKUP_OUTCOME: ${{ steps.slack_user.outcome }}
159+
SLACK_DM_OUTCOME: ${{ steps.slack_dm.outcome }}
160+
SLACK_DM_OK: ${{ steps.slack_dm.outputs.ok }}
161+
SLACK_DM_RESPONSE: ${{ steps.slack_dm.outputs.response }}
162+
SLACK_DM_TS: ${{ steps.slack_dm.outputs.ts }}
163+
run: |
164+
echo "Slack user lookup outcome: ${SLACK_LOOKUP_OUTCOME:-not run}"
165+
if [ -z "$SLACK_USER_ID" ]; then
166+
echo "Slack DM skipped because no Slack user ID was resolved."
167+
elif [ "$SLACK_DM_OK" = "true" ]; then
168+
echo "Slack DM sent successfully (timestamp: ${SLACK_DM_TS:-unavailable})."
169+
else
170+
echo "Slack DM did not return success (step outcome: ${SLACK_DM_OUTCOME:-unknown})."
171+
if [ -n "$SLACK_DM_RESPONSE" ]; then
172+
printf '%s\n' "$SLACK_DM_RESPONSE" | jq -r 'if type == "object" and (.error | type) == "string" then "Slack API error: " + .error else "Slack API response did not include an error field." end'
173+
else
174+
echo "Slack DM API response was unavailable."
175+
fi
176+
fi
177+
178+
- name: Notify the docs-open-source channel
179+
id: slack_channel_notification
180+
if: ${{ !cancelled() && steps.membership_check.outputs.did_warn == 'true' }}
70181
uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0
71182
env:
72-
SLACK_MESSAGE: <@${{ github.actor }}> opened https://github.com/github/docs/issues/${{ github.event.number || github.event.issue.number }} publicly on the github/docs repo instead of a private repo. They have been notified via a new issue in the private repo to confirm this was intentional.
73183
SLACK_CHANNEL_ID: ${{ secrets.DOCS_OPEN_SOURCE_SLACK_CHANNEL_ID }}
184+
SLACK_MESSAGE: ${{ format('{0} opened {1} publicly in github/docs instead of the private repo. An internal confirmation issue was created.', steps.slack_user.outputs.slack_mention, github.event.issue.html_url || github.event.pull_request.html_url) }}
74185
with:
75186
method: chat.postMessage
76187
token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }}
@@ -79,6 +190,25 @@ jobs:
79190
channel: ${{ toJSON(env.SLACK_CHANNEL_ID) }}
80191
text: ${{ toJSON(env.SLACK_MESSAGE) }}
81192
193+
- name: Log Slack channel notification result
194+
if: ${{ !cancelled() && steps.membership_check.outputs.did_warn == 'true' }}
195+
env:
196+
SLACK_CHANNEL_OUTCOME: ${{ steps.slack_channel_notification.outcome }}
197+
SLACK_CHANNEL_OK: ${{ steps.slack_channel_notification.outputs.ok }}
198+
SLACK_CHANNEL_RESPONSE: ${{ steps.slack_channel_notification.outputs.response }}
199+
SLACK_CHANNEL_TS: ${{ steps.slack_channel_notification.outputs.ts }}
200+
run: |
201+
if [ "$SLACK_CHANNEL_OK" = "true" ]; then
202+
echo "Slack channel notification sent successfully (timestamp: ${SLACK_CHANNEL_TS:-unavailable})."
203+
else
204+
echo "Slack channel notification did not return success (step outcome: ${SLACK_CHANNEL_OUTCOME:-unknown})."
205+
if [ -n "$SLACK_CHANNEL_RESPONSE" ]; then
206+
printf '%s\n' "$SLACK_CHANNEL_RESPONSE" | jq -r 'if type == "object" and (.error | type) == "string" then "Slack API error: " + .error else "Slack API response did not include an error field." end'
207+
else
208+
echo "Slack channel API response was unavailable."
209+
fi
210+
fi
211+
82212
- name: Check out repo
83213
if: ${{ failure() && github.event_name != 'pull_request_target' }}
84214
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

‎.github/workflows/sync-sdk-docs.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,12 @@ jobs:
261261

262262
- uses: ./.github/actions/create-workflow-failure-issue
263263
id: create-failure-issue
264-
if: ${{ failure() && github.event_name != 'workflow_dispatch' }}
264+
if: ${{ failure() && github.event_name != 'workflow_dispatch' && github.event_name != 'pull_request' }}
265265
with:
266266
token: ${{ secrets.DOCS_BOT_PAT_BASE }}
267267

268268
- uses: ./.github/actions/slack-alert
269-
if: ${{ failure() && github.event_name != 'workflow_dispatch' }}
269+
if: ${{ failure() && github.event_name != 'workflow_dispatch' && github.event_name != 'pull_request' }}
270270
with:
271271
slack_token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }}
272272
issue_url: ${{ steps.create-failure-issue.outputs.issue_url }}

‎.github/workflows/test.yml‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,29 @@ jobs:
140140
if: ${{ matrix.name == 'content-linter' }}
141141
run: echo "${{ steps.changed_files.outputs.all_modified_files }}" > get_diff_files.txt
142142

143+
- name: Check for automated pipeline changes
144+
if: ${{ matrix.name == 'automated-pipelines' && github.event_name == 'pull_request' }}
145+
id: pipeline_files
146+
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
147+
with:
148+
# Count a rename as a delete plus an add, so moving a file out of these paths still matches.
149+
output_renamed_files_as_deleted_and_added: true
150+
# Generated content counts too, so sync PRs render every page.
151+
files: |
152+
.github/workflows/test.yml
153+
package-lock.json
154+
content/code-security/reference/code-scanning/codeql/codeql-cli-manual/**
155+
content/graphql/**
156+
content/rest/**
157+
src/audit-logs/**
158+
src/automated-pipelines/**
159+
src/codeql-cli/**
160+
src/github-apps/**
161+
src/graphql/**
162+
src/rest/**
163+
src/secret-scanning/**
164+
src/webhooks/**
165+
143166
- uses: ./.github/actions/cache-nextjs
144167
if: ${{ env.START_VITEST_SERVER != 'false' }}
145168

@@ -172,4 +195,6 @@ jobs:
172195
# RUNNER_DEBUG enables timing and path logs when Actions reruns jobs with debug logging.
173196
RUNNER_DEBUG: ${{ runner.debug }}
174197
VITEST_FLAGS: ${{ matrix.name == 'article-api' && '--no-file-parallelism --maxWorkers=1' || '' }}
198+
# PRs render pinned autogenerated pages unless they change pipeline code or output.
199+
AUTOGENERATED_RENDER_ALL: ${{ matrix.name == 'automated-pipelines' && (github.event_name != 'pull_request' || steps.pipeline_files.outputs.any_modified == 'true') }}
175200
run: npm test -- $VITEST_FLAGS src/${{ matrix.name }}/tests/

‎content/account-and-profile/how-tos/account-management/deleting-your-personal-account.md‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Deleting your personal account
3-
intro: You can delete your personal account on {% data variables.location.product_location %} at any time.
3+
intro: You can delete your personal account on {% data variables.location.product_location %} from your account settings.
44
redirect_from:
55
- /articles/deleting-a-user-account
66
- /articles/deleting-your-user-account
@@ -46,6 +46,14 @@ Before you delete your personal account, make a copy of all repositories, privat
4646
> [!WARNING]
4747
> Once your personal account has been deleted, {% ifversion fpt or ghec %}{% data variables.product.company_short %}{% elsif ghes %}an enterprise owner{% endif %} cannot restore your content.
4848
49+
{% ifversion fpt or ghec %}
50+
51+
If your account is flagged, suspended, or otherwise restricted, **Delete your account** may be unavailable, disabled, or direct you to {% data variables.contact.github_support %} instead of completing deletion. Contact {% data variables.contact.contact_support_page %} to request access to the normal self-service deletion flow.
52+
53+
{% data variables.contact.github_support %} does not delete your personal account on your behalf. Once access is restored, you must complete the steps below to delete your account.
54+
55+
{% endif %}
56+
4957
{% data reusables.user-settings.access_settings %}
5058
{% data reusables.user-settings.account_settings %}
5159
1. At the bottom of the Account Settings page, under "Delete account", click **Delete your account**. Before you can delete your personal account:

‎content/billing/how-tos/troubleshooting/locked-account.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ category:
2626

2727
You can unlock and access your account by updating your account's payment method and resuming paid status. We do not ask you to pay for the time elapsed in locked mode.
2828

29+
A failed authorization hold when you try to sign up for {% data variables.product.prodname_copilot_short %} or increase a budget can also lock your personal account billing and block {% data variables.product.prodname_actions %} or other free-tier access. This can happen even if you plan to stay on {% data variables.product.prodname_free_user %}, have removed the payment method, or see no active {% data variables.product.prodname_copilot_short %} plan or balance in the "Billing and licensing" section under your account's settings page.
30+
2931
You can downgrade your user account or organization to {% data variables.product.prodname_free_team %} to continue with the same advanced features in public repositories. For more information, see [AUTOTITLE](/billing/how-tos/manage-plan-and-licenses/downgrade-plan).
3032

3133
## Unlocking an account's features due to a declined payment
@@ -40,8 +42,10 @@ If the new billing information is approved, we will immediately charge you for t
4042

4143
When initiating a subscription or trial, we verify the entered payment method using an authorization hold. If the hold is unsuccessful, the chosen subscription or trial will not activate.
4244

43-
If your account's advanced features are locked due to a failed authorization hold, you'll need to update your billing information to trigger a new authorization.
45+
If you want to continue with the chosen subscription, update your billing information to trigger a new authorization.
4446

4547
{% data reusables.billing.manage-payment-info %}
4648

4749
If the new authorization is successful, the account will automatically unlock and you will be able to proceed with the chosen subscription.
50+
51+
If the "Billing and licensing" section under your account's settings page shows no active subscription, balance, or payment issue to resolve, contact {% data variables.contact.contact_support %} to request a review of the billing lock.

0 commit comments

Comments
 (0)