-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[DO NOT MERGE] Astro reorg scripts #38325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jhgilbert
wants to merge
74
commits into
master
Choose a base branch
from
jen.gilbert/astro-reorg-scripts
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
74 commits
Select commit
Hold shift + click to select a range
80b2cbf
Add reorg scripts and config
jhgilbert fb3d0a6
Review code and fill gaps
jhgilbert d140143
Organize reorg files
jhgilbert d511ddb
Rename file
jhgilbert 5ee8f26
Rename files
jhgilbert 93e4efb
Add file to config
jhgilbert 7e4fef0
Add visual diffs for file updates
jhgilbert 6092c83
Skip .gitignore previews
jhgilbert 4ff7a32
Simplify script
jhgilbert b896be1
Code tweaks
jhgilbert 883496e
Handle Python env
jhgilbert 5247332
Simplify rollback script
jhgilbert 902a46b
Tweak script name
jhgilbert fc68cb1
Add test plan
jhgilbert ee54d63
Merge remote-tracking branch 'origin/master' into jen.gilbert/astro-r…
jhgilbert 933278d
Merge branch 'master' into jen.gilbert/astro-reorg-scripts
jhgilbert 87841fd
Tweak rollback script
jhgilbert 7251884
Tweak test PR script
jhgilbert 218f70d
Tweak script
jhgilbert 86ca7b4
Ignore untracked astro folder
jhgilbert 18259b9
Tweak test PR setup
jhgilbert a5cffa5
Use mock-master instead of master to yield small PR diffs
jhgilbert 1c8ff95
Move test branch names to config file and increase overall safety
jhgilbert 94fcd9a
Make label creation idempotent
jhgilbert 2991eb1
Merge branch 'master' into jen.gilbert/astro-reorg-scripts
jhgilbert 363e9b8
Tweak config naming
jhgilbert b0725ab
Turn rename detection off in the mergeability check
jhgilbert cb33f3c
Add do not merge label to all test PRs
jhgilbert f453624
Tweaks
jhgilbert f5ddaef
Tweak handling of GitHub actions
jhgilbert d13d132
Sync validation script
jhgilbert e3725d6
Align resolution script with contributor instructions
jhgilbert 02b9fcc
Update mock master branch name
jhgilbert 8f46746
Resolve script errors
jhgilbert b509301
Resolve script errors
jhgilbert 1d681f9
Add test setup instructions
jhgilbert 4f49525
Tweak test setup instructions
jhgilbert 5fa9ce9
Set new mock reorged master branch
jhgilbert 1b9ad6d
Tweak test cases
jhgilbert d447fb7
Update mock reorged master branch
jhgilbert 4f793fd
Add clearer labels/PR titles
jhgilbert 2dec23f
Add link for viewing auto-fixed PRs
jhgilbert 83375b1
Add link for viewing auto-fixed PRs
jhgilbert c3f96ce
Update the mock reorged master branch name
jhgilbert a980130
Fix syntax error
jhgilbert cbafe29
Update mock reorged master branch name
jhgilbert bf015bf
Tweak labeling approach
jhgilbert 60e693a
Require a PR limit
jhgilbert 1a94041
Tweak skip cases
jhgilbert 7c038cb
Add test cases
jhgilbert 0fde77f
Merge branch 'master' into jen.gilbert/astro-reorg-scripts
jhgilbert ae93469
Reduce staleness to 1 month and close autofixed PRs
jhgilbert 889525d
Tweak comment verbiage
jhgilbert 0627c6b
Add repo reorg readme
jhgilbert c4fc90c
Add generic stale PR handling scripts
jhgilbert f4b93f3
Tweak automatic conflict resolution flow
jhgilbert d1f7da7
Tweak reorg UI
jhgilbert 58e9f5d
Update reorg docs
jhgilbert 05a59d4
Update name of mock reorged master branch
jhgilbert 2ad9af9
Tweak scripts
jhgilbert 9551a98
Add reorg steps
jhgilbert 4c55673
Finish initial execution steps
jhgilbert 99915de
Resolve inconsistencies
jhgilbert a17dd11
Tweak comments
jhgilbert 78aec3f
Add mermaid diagram
jhgilbert 6ab7cce
Update mock master reorg branch name
jhgilbert 21c5beb
Update mock master branch name
jhgilbert fe65d49
Fix broken ignore negation on build config files
jhgilbert c66cb83
Update reorg execution steps
jhgilbert e39c9cb
Tweak PR comment verbiage
jhgilbert 6258c17
Move generated PR list to a more appropriate folder
jhgilbert ce3cb3f
Code review
jhgilbert a8fbd35
Code review
jhgilbert 1171ea6
Update the name of the mock master branch
jhgilbert File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # `documentation` repository reorg | ||
|
|
||
| # Reorg status | ||
|
|
||
| Not yet merged. | ||
|
|
||
| # Project overview | ||
|
|
||
| We're gradually migrating the Datadog docs site from Hugo to Astro. To support this, we must reorganize the `documentation` repo into a monorepo that supports two sites (`hugo` and `astro`), instead of having the entire repo serve as our Hugo site. | ||
|
|
||
| **This reorg causes conflicts in almost every open PR in the `documentation` repo.** | ||
|
|
||
| ## What does the reorg change? | ||
|
|
||
| The most impactful change is that any Hugo-specific site files move to a `hugo` folder instead of being located at the top level of the repo. This change impacts thousands of files, and causes the majority of conflicts. | ||
|
|
||
| For a full list of repo files and folders and their updated location, see [the configuration file for the reorg script](https://github.com/DataDog/documentation/blob/jen.gilbert/astro-reorg-scripts/astro_reorg/config.yaml). | ||
|
|
||
| # Handling PR conflicts | ||
|
|
||
| Once the reorg is merged to master, all open PRs are processed. Conflicts are fixed automatically where possible. When an automatic fix is not possible, we defer to the author's best judgment in resolving the conflicts, but offer escalation options in the PR comments in case they need help. | ||
|
|
||
| ## PR cases | ||
|
|
||
| * **Skipped because it has no conflicts:**: We do not try to autofix the PR because it doesn't have any conflicts. We add a label as a guard against further processing. | ||
| * **Skipped as a WIP:**: We do not try to autofix the PR because it has the `WORK IN PROGRESS` label. We leave a comment describing how to queue the PR for autofix processing if desired. | ||
| * **Autofixed PR:** We fix the conflicts automatically, close the PR, and link a new PR for the author's review. | ||
| * **Stale PR:** We do not act on the PR, because it appears to be stale. We leave a comment describing how to queue the PR for autofix processing if desired. | ||
| * **Manual fix required:** We cannot resolve the conflicts automatically. We leave a comment describing why the conflicts are occurring, and how to ask for help if needed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # Docs repository reorg context | ||
|
|
||
| This repo is currently a Hugo site. We instead want it to contain a `hugo` and `astro` site side by side, with no overlap in their envs, `package.json` files, etc. | ||
|
|
||
| `astro_reorg/config.yaml` describes the relocation target for every file and folder at the top level of the repo. | ||
|
|
||
| `astro_reorg/execute_reorg.py` implements the file and folder path changes, and updates any dependencies on those paths, such as GitHub actions, CODEOWNERS, and Husky workflows. | ||
|
|
||
| `astro_reorg/helpers.py` contains utilities (path manipulation, git/shell helpers, YAML config loading) used by `validate_reorg.py`. The other scripts predate it and each define their own small helpers inline. | ||
|
|
||
| `astro_reorg/local_rollback.py` functions as an "undo" action for the reorg: removes `hugo/` and restores `.gitignore`, `.github/`, and `.husky/` from git. | ||
|
|
||
| `astro_reorg/validate_reorg.py` verifies the functionality of affected entities where possible. | ||
|
|
||
| `astro_reorg/resolve_pr_conflicts.py` finds open PRs with merge conflicts caused by the reorg, and either auto-fixes them (by replaying commits at post-reorg paths) or labels them for manual review. It defaults to a dry run against a mock base branch, so it can't touch real PRs by accident. | ||
|
|
||
| `astro_reorg/create_test_prs.py` creates test PRs for exercising `resolve_pr_conflicts.py`, including the conflicts it can't auto-fix. Each spec in `TEST_PRS` becomes a PR that reproduces one conflict scenario. | ||
|
|
||
| Both scripts share the `test:` section of `config.yaml`, which names the branches a test run uses: `mock_reorged_master_branch` (the stand-in for post-reorg `master` that test PRs target and the resolver treats as the base) and `branch_from` (the frozen snapshot the test PRs are cut from). | ||
|
|
||
| For flags and the mechanics of each scenario, read the script's module docstring and the comments on `TEST_PRS` — that's the source of truth, so this file doesn't have to track them. | ||
|
|
||
| You can ignore the `astro` folder, it's a remnant from another branch where an Astro site is being developed. It is completely out of scope. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| #!/usr/bin/env python3 | ||
| """ | ||
| Close open PRs that carry the `autolabeled-stale` label. | ||
| Posts a comment before closing. Defaults to a dry run where it just reports | ||
| what would be done. | ||
|
|
||
| Use the GitHub label query to review which PRs are queued for closure: | ||
| https://github.com/DataDog/documentation/pulls?q=is%3Aopen+label%3Aautolabeled-stale | ||
|
|
||
| Usage: | ||
| python3 astro_reorg/close_stale_prs.py [--no-dry-run] [--pr NUMBER ...] [--limit N] | ||
|
|
||
| Flags: | ||
| --no-dry-run Actually close PRs instead of just reporting what would be done. | ||
| --pr NUMBER ... Only process the given PR number(s). | ||
| --limit N Stop after closing N PRs. PRs that are skipped don't count toward | ||
| the limit. Use it to roll out gradually: start with --limit 1, review | ||
| the results, then raise it as you gain confidence. | ||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| import argparse | ||
| import json | ||
| import subprocess | ||
| import sys | ||
| from datetime import datetime, timedelta, timezone | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Configuration | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| REPO = "DataDog/documentation" | ||
|
|
||
| LABEL_STALE = "autolabeled-stale" | ||
|
|
||
| CLOSE_COMMENT = ( | ||
| "Closing this PR because it has had no activity for six months. " | ||
| "If this was done in error, feel free to re-open the PR." | ||
| ) | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Shell helpers | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| def run(cmd: list[str]) -> subprocess.CompletedProcess: | ||
| return subprocess.run(cmd, capture_output=True, text=True) | ||
|
|
||
|
|
||
| def gh_json(*args: str) -> object: | ||
| result = run(["gh", *args]) | ||
| if result.returncode != 0: | ||
| raise RuntimeError(f"gh {' '.join(args[:3])}: {result.stderr.strip()}") | ||
| return json.loads(result.stdout) if result.stdout.strip() else [] | ||
|
|
||
|
|
||
| def gh_run(*args: str) -> str: | ||
| result = run(["gh", *args]) | ||
| if result.returncode != 0: | ||
| raise RuntimeError(f"gh {' '.join(args[:3])}: {result.stderr.strip()}") | ||
| return result.stdout | ||
|
|
||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # GitHub helpers | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| def post_comment(pr_number: int, body: str, dry_run: bool) -> None: | ||
| if dry_run: | ||
| print(f" [dry-run] would comment on PR #{pr_number}:\n {body[:120]}") | ||
| return | ||
| gh_run("pr", "comment", str(pr_number), "--repo", REPO, "--body", body) | ||
| print(f" Posted comment on PR #{pr_number}") | ||
|
|
||
|
|
||
| def close_pr(pr_number: int, dry_run: bool) -> None: | ||
| if dry_run: | ||
| print(f" [dry-run] would close PR #{pr_number}") | ||
| return | ||
| gh_run("pr", "close", str(pr_number), "--repo", REPO) | ||
| print(f" Closed PR #{pr_number}") | ||
|
|
||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Per-PR processing | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| def process_pr(pr: dict, dry_run: bool) -> bool: | ||
| """Comment and close a labeled PR. Return True if we acted, False if we skipped.""" | ||
| pr_number = pr["number"] | ||
| title = pr["title"] | ||
|
|
||
| print(f"\nPR #{pr_number}: {title}") | ||
| print(f" Last updated: {pr.get('updatedAt', 'unknown')}") | ||
|
|
||
| post_comment(pr_number, CLOSE_COMMENT, dry_run) | ||
| close_pr(pr_number, dry_run) | ||
| return True | ||
|
|
||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Entry point | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| def get_labeled_prs(only: list[int] | None = None, fetch_limit: int = 1000) -> list[dict]: | ||
| """Return open PRs carrying the stale label, optionally filtered to specific numbers.""" | ||
| fields = "number,title,updatedAt" | ||
| if only: | ||
| prs = [] | ||
| for n in only: | ||
| pr = gh_json("pr", "view", str(n), "--repo", REPO, "--json", fields) | ||
| prs.append(pr) | ||
| return prs # type: ignore[return-value] | ||
| return gh_json( # type: ignore[return-value] | ||
| "pr", "list", "--repo", REPO, "--state", "open", | ||
| "--label", LABEL_STALE, | ||
| "--json", fields, "--limit", str(fetch_limit), | ||
| ) | ||
|
|
||
|
|
||
| def main() -> None: | ||
| parser = argparse.ArgumentParser( | ||
| description=f"Close open PRs labeled {LABEL_STALE!r}.", | ||
| formatter_class=argparse.RawDescriptionHelpFormatter, | ||
| epilog=__doc__, | ||
| ) | ||
| parser.add_argument( | ||
| "--dry-run", action=argparse.BooleanOptionalAction, default=True, | ||
| help="Report what would be done without making any changes (default: on). " | ||
| "Use --no-dry-run to apply changes.", | ||
| ) | ||
| parser.add_argument( | ||
| "--pr", type=int, action="append", dest="prs", metavar="NUMBER", | ||
| help="Only process this PR number (may be repeated).", | ||
| ) | ||
| parser.add_argument( | ||
| "--limit", type=int, default=None, metavar="N", | ||
| help="Stop after closing N PRs. Skipped PRs don't count toward the limit. " | ||
| "Use it to roll out gradually: start at 1, review, then raise it.", | ||
| ) | ||
| args = parser.parse_args() | ||
|
|
||
| if args.limit is not None and args.limit < 1: | ||
| parser.error("--limit must be a positive integer.") | ||
|
|
||
| if args.dry_run: | ||
| print("DRY-RUN mode — no PRs will be modified.\n") | ||
|
|
||
| prs = get_labeled_prs(args.prs) | ||
| print(f"Found {len(prs)} labeled PR(s) to close.") | ||
| if args.limit is not None: | ||
| print(f"Limit: will stop after closing {args.limit} PR(s).") | ||
|
|
||
| acted = 0 | ||
| for pr in prs: | ||
| if args.limit is not None and acted >= args.limit: | ||
| print(f"\nReached --limit of {args.limit} closed PR(s) — stopping.") | ||
| break | ||
| try: | ||
| if process_pr(pr, args.dry_run): | ||
| acted += 1 | ||
| except Exception as exc: | ||
| print(f"\nERROR processing PR #{pr.get('number', '?')}: {exc}", | ||
| file=sys.stderr) | ||
| print(" Skipping to the next PR.", file=sys.stderr) | ||
|
|
||
| print(f"\nClosed {acted} PR(s). Done.") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| # Files and folders that stay at the top level (not moved into hugo/). | ||
| # Everything else moves into hugo/. | ||
|
|
||
| top_level: | ||
| # Sites | ||
| - astro | ||
|
|
||
| # Repo scripts | ||
| - astro_reorg | ||
| - reorg_issues.md | ||
|
|
||
| # Created by reorg.py as the move target | ||
| - hugo | ||
|
|
||
| # Git / CI / repo-wide tooling | ||
| - .git | ||
| - .github | ||
| - .gitlab-ci.yml | ||
| - .gitignore | ||
| - .gitattributes | ||
| - .husky | ||
| - .claude | ||
|
|
||
| # Editor / IDE config | ||
| - .editorconfig | ||
| - .vscode | ||
|
|
||
| # Repo docs | ||
| - README.md | ||
| - LICENSE | ||
| - CONTRIBUTING.md | ||
| - CLAUDE.md | ||
| - REPO_REORG.md | ||
|
|
||
| # Linting / formatting (shared) | ||
| - .eslintrc | ||
| - .eslintignore | ||
| - .vale.ini | ||
| - .rubocop.yml | ||
| - .prettierignore | ||
| - prettier.config.js | ||
| - static-analysis.datadog.yml | ||
|
|
||
| # Datadog CI / synthetics | ||
| - datadog-ci.preview.json | ||
| - general.preview.synthetics.json | ||
| - repository.datadog.yml | ||
|
|
||
| # Docker | ||
| - docker-compose-docs.yml | ||
|
|
||
| moves_to_hugo: | ||
| # Hugo core | ||
| - archetypes | ||
| - assets | ||
| - config | ||
| - content | ||
| - data | ||
| - i18n | ||
| - layouts | ||
| - static | ||
| - resources | ||
| - public | ||
|
|
||
| # Hugo build tooling | ||
| - Makefile | ||
| - Makefile.config | ||
| - Makefile.config.example | ||
| - local | ||
| - hugpython | ||
| - gradle | ||
| - _vendor | ||
|
|
||
| # Hugo build artifacts / state | ||
| - .hugo_build.lock | ||
| - logs | ||
| - integrations_data | ||
|
|
||
| # Node / JS (Hugo-specific) | ||
| - babel.config.js | ||
| - jest.config.js | ||
| - postcss.config.js | ||
| - markdoc.config.json | ||
| - customization_config | ||
|
|
||
| # Go module — powers Hugo Modules (imports websites-modules, vendors into | ||
| # _vendor/). Hugo requires go.mod in the project root; Astro uses no Go. | ||
| - go.mod | ||
| - go.sum | ||
|
|
||
| # Node / Yarn (Hugo site owns these; Astro has its own under astro/) | ||
| - package.json | ||
| - package-lock.json | ||
| - yarn.lock | ||
| - .yarnrc.yml | ||
| - .yarn | ||
| - node_modules | ||
| - .nvmrc | ||
|
|
||
| # Testing (Hugo-specific) | ||
| - playwright.config.ts | ||
| - playwright-report | ||
| - test-results | ||
| - e2e | ||
|
|
||
| # Content tooling | ||
| - translate.yaml | ||
| - .translate | ||
| - typesense.config.json | ||
| - .htmltest.yml | ||
| - .apigentools-info | ||
| - agent_config_types_list.txt | ||
|
|
||
| # Examples / docs | ||
| - examples | ||
| - docs | ||
| - plans | ||
|
|
||
| # Static assets (Hugo) | ||
| - usage-notifications-email.png | ||
|
|
||
| ignore: | ||
| # macOS metadata | ||
| - .DS_Store | ||
| # Python bytecode cache | ||
| - __pycache__ | ||
|
|
||
| # Branches used to exercise the reorg tooling against a fake main instead of the | ||
| # real master. create_test_prs.py opens test PRs against mock_reorged_master_branch, and | ||
| # resolve_pr_conflicts.py treats that same branch as the post-reorg base by | ||
| # default (pass --live to target the real master), so the two scripts always | ||
| # agree on which branches to use during a test run. | ||
| test: | ||
| # Stand-in for post-reorg master: the branch that has the reorg applied. Test | ||
| # PRs target it, and resolve_pr_conflicts.py treats it as the base by default. | ||
| mock_reorged_master_branch: mock-reorged-master-7-20-1231 | ||
| # Frozen snapshot of master that test PRs branch off, so each PR's diff stays | ||
| # small (a PR shows every commit in its head that isn't in the base branch). | ||
| branch_from: jen.gilbert/astro-reorg-scripts |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would fail if I ran it cos I have a few more gitignored files/directories that I've assembled over the years that aren't here, but I could add them if I need to run the script.