Skip to content

Commit 3dd8235

Browse files
committed
refactor(scripts): reorganize and rename copy-discussions to migrate-discussions
- Move script to migrate-discussions/ subdirectory structure - Rename from copy-discussions.js to migrate-discussions.js - Update documentation to reflect migration terminology - Add package.json with octokit dependency - Enhance README with rate limiting and resume features - Update .gitignore to allow nested package.json files
1 parent 6689cf3 commit 3dd8235

File tree

6 files changed

+894
-60
lines changed

6 files changed

+894
-60
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
**/.DS_Store
44
*.pem
55
*.json
6+
!/scripts/*/package*.json
67
node_modules*
78
test*.js
89
test*.sh

scripts/README.md

Lines changed: 4 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -36,66 +36,6 @@ Configuration values to change in the script:
3636

3737
Migrate work items from Azure DevOps to GitHub issues - this just links out to a [separate repo](https://github.com/joshjohanning/ado_workitems_to_github_issues)
3838

39-
## copy-discussions.js
40-
41-
Copy GitHub Discussions between repositories, including categories, labels, comments, and replies. This script can copy discussions across different GitHub instances and enterprises.
42-
43-
The script is expecting:
44-
45-
- environment variables `SOURCE_TOKEN` and `TARGET_TOKEN` with GitHub PATs that have `repo` and `read:discussion` scopes
46-
- dependencies installed via `npm i octokit`
47-
48-
Script usage:
49-
50-
```bash
51-
export SOURCE_TOKEN=ghp_abc
52-
export TARGET_TOKEN=ghp_xyz
53-
npm i octokit
54-
node ./copy-discussions.js source-org source-repo target-org target-repo
55-
```
56-
57-
Optional environment variables:
58-
59-
- `SOURCE_API_URL` - API endpoint for source (defaults to `https://api.github.com`)
60-
- `TARGET_API_URL` - API endpoint for target (defaults to `https://api.github.com`)
61-
62-
Example with GitHub Enterprise Server:
63-
64-
```bash
65-
export SOURCE_API_URL=https://github.mycompany.com/api/v3
66-
export TARGET_API_URL=https://api.github.com
67-
export SOURCE_TOKEN=ghp_abc
68-
export TARGET_TOKEN=ghp_xyz
69-
npm i octokit
70-
node ./copy-discussions.js source-org source-repo target-org target-repo
71-
```
72-
73-
Features:
74-
75-
- Automatically creates missing discussion categories in the target repository
76-
- Creates labels in the target repository if they don't exist
77-
- Copies all comments and threaded replies with proper attribution
78-
- Copies poll results as static snapshots (with table and optional Mermaid chart)
79-
- Preserves reaction counts on discussions, comments, and replies
80-
- Maintains locked status of discussions
81-
- Indicates pinned discussions with a visual indicator
82-
- Handles rate limiting with exponential backoff
83-
- Provides colored console output for better visibility
84-
85-
Configuration:
86-
87-
- Set `INCLUDE_POLL_MERMAID_CHART = false` at the top of the script to disable Mermaid pie charts for polls
88-
89-
Notes:
90-
91-
- If a category doesn't exist in the target repository, discussions will be created in the "General" category
92-
- The script preserves discussion metadata by adding attribution text to the body and comments
93-
- Poll results are copied as static snapshots - voting is not available in copied discussions
94-
- Reactions are copied as read-only summaries (users cannot add new reactions)
95-
- Locked discussions will be locked in the target repository
96-
- Pinned status is indicated in the discussion body (GitHub API doesn't allow pinning via GraphQL)
97-
- Both source and target repositories must have GitHub Discussions enabled
98-
9939
## delete-branch-protection-rules.ps1
10040

10141
Delete branch protection rules programmatically based on a pattern.
@@ -151,6 +91,10 @@ My use case is to use this list to determine who needs to be added to a organiza
15191
1. Run: `./new-users-to-add-to-project.sh <org> <repo> <file>`
15292
2. Don't delete the `<file>` as it functions as your user database
15393

94+
## migrate-discussions
95+
96+
See: [migrate-discussions](./migrate-discussions/README.md)
97+
15498
## migrate-docker-containers-between-github-instances.sh
15599

156100
Migrate Docker Containers in GitHub Packages (GitHub Container Registry) from one GitHub organization to another.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# migrate-discussions.js
2+
3+
Migrate GitHub Discussions between repositories, including categories, labels, comments, and replies. This script can migrate discussions across different GitHub instances and enterprises with comprehensive rate limit handling and resume capabilities.
4+
5+
## Prerequisites
6+
7+
- `SOURCE_TOKEN` environment variable with GitHub PAT that has `repo` scope and read access to source repository discussions
8+
- `TARGET_TOKEN` environment variable with GitHub PAT that has `repo` scope and write access to target repository discussions
9+
- Dependencies installed via `npm i octokit`
10+
- Both source and target repositories must have GitHub Discussions enabled
11+
12+
## Script usage
13+
14+
Basic usage:
15+
16+
```bash
17+
export SOURCE_TOKEN=ghp_abc
18+
export TARGET_TOKEN=ghp_xyz
19+
npm i octokit
20+
node ./migrate-discussions.js source-org source-repo target-org target-repo
21+
```
22+
23+
Resume from a specific discussion number (useful if interrupted):
24+
25+
```bash
26+
node ./migrate-discussions.js source-org source-repo target-org target-repo --start-from 50
27+
```
28+
29+
## Optional environment variables
30+
31+
- `SOURCE_API_URL` - API endpoint for source (defaults to `https://api.github.com`)
32+
- `TARGET_API_URL` - API endpoint for target (defaults to `https://api.github.com`)
33+
34+
Example with GitHub Enterprise Server:
35+
36+
```bash
37+
export SOURCE_API_URL=https://github.mycompany.com/api/v3
38+
export TARGET_API_URL=https://api.github.com
39+
export SOURCE_TOKEN=ghp_abc
40+
export TARGET_TOKEN=ghp_xyz
41+
npm i octokit
42+
node ./migrate-discussions.js source-org source-repo target-org target-repo
43+
```
44+
45+
## Features
46+
47+
### Content Migration
48+
49+
- Automatically creates missing discussion categories in the target repository
50+
- Creates labels in the target repository if they don't exist
51+
- Copies all comments and threaded replies with proper attribution
52+
- Copies poll results as static snapshots (with table and optional Mermaid chart)
53+
- Preserves reaction counts on discussions, comments, and replies
54+
- Maintains locked status of discussions
55+
- Indicates pinned discussions with a visual indicator
56+
- Marks answered discussions and preserves the accepted answer
57+
58+
### Rate Limiting & Reliability
59+
60+
- **Automatic rate limit handling** with Octokit's built-in throttling plugin
61+
- **Intelligent retry logic** for both primary and secondary rate limits (up to 3 retries)
62+
- **GitHub-recommended delays** - 3 seconds between discussions/comments to stay under secondary rate limits
63+
- **Resume capability** - Use `--start-from <number>` to resume from a specific discussion if interrupted
64+
- **Rate limit tracking** - Summary shows how many times primary and secondary rate limits were hit
65+
66+
### User Experience
67+
68+
- Colored console output with timestamps for better visibility
69+
- Comprehensive summary statistics at completion
70+
- Detailed progress logging for each discussion, comment, and reply
71+
72+
## Configuration options
73+
74+
Edit these constants at the top of the script:
75+
76+
- `INCLUDE_POLL_MERMAID_CHART` - Set to `false` to disable Mermaid pie charts for polls (default: `true`)
77+
- `RATE_LIMIT_SLEEP_SECONDS` - Sleep duration between API calls (default: `0.5` seconds)
78+
- `DISCUSSION_PROCESSING_DELAY_SECONDS` - Delay between processing discussions/comments (default: `3` seconds)
79+
- `MAX_RETRIES` - Maximum retries for non-rate-limit errors (default: `3`)
80+
81+
## Summary output
82+
83+
After completion, the script displays comprehensive statistics:
84+
85+
- Total discussions found and created
86+
- Discussions skipped (when using `--start-from`)
87+
- Total comments found and copied
88+
- **Primary rate limits hit** - How many times the script hit GitHub's primary rate limit
89+
- **Secondary rate limits hit** - How many times the script hit GitHub's secondary rate limit
90+
- List of missing categories that need manual creation
91+
92+
## Notes
93+
94+
### Category handling
95+
96+
- If a category doesn't exist in the target repository, discussions will be created in the "General" category as a fallback
97+
- Missing categories are tracked and reported at the end of the script
98+
99+
### Content preservation
100+
101+
- The script preserves discussion metadata by adding attribution text to the body and comments
102+
- Poll results are copied as static snapshots - voting is not available in copied discussions
103+
- Reactions are copied as read-only summaries (users cannot add new reactions to the migrated content)
104+
- Attachments (images and files) will not copy over and require manual handling
105+
106+
### Discussion states
107+
108+
- Locked discussions will be locked in the target repository
109+
- Closed discussions will be closed in the target repository
110+
- Answered discussions will have the same comment marked as the answer
111+
- Pinned status is indicated in the discussion body (GitHub API doesn't allow pinning via GraphQL)
112+
113+
### Rate limiting
114+
115+
- GitHub limits content-generating requests to avoid abuse
116+
- No more than 80 content-generating requests per minute
117+
- No more than 500 content-generating requests per hour
118+
- The script stays under 1 discussion or comment created every 3 seconds (GitHub's recommendation)
119+
- Automatic retry with wait times from GitHub's `retry-after` headers
120+
- If rate limits are consistently hit, the script will retry up to 3 times before failing
121+
122+
### Resume capability
123+
124+
- Use `--start-from <number>` to skip discussions before a specific discussion number
125+
- Useful for resuming after interruptions or failures
126+
- Discussion numbers are the user-friendly numbers (e.g., #50), not GraphQL IDs

scripts/copy-discussions.js renamed to scripts/migrate-discussions/migrate-discussions.js

File renamed without changes.

0 commit comments

Comments
 (0)