Skip to content

Proof of concept: Remove the requirement to check out the gutenberg repository and running the full build script#11019

Open
desrosj wants to merge 26 commits intoWordPress:trunkfrom
desrosj:try/remove-gutenberg-git-checkout-and-build
Open

Proof of concept: Remove the requirement to check out the gutenberg repository and running the full build script#11019
desrosj wants to merge 26 commits intoWordPress:trunkfrom
desrosj:try/remove-gutenberg-git-checkout-and-build

Conversation

@desrosj
Copy link
Member

@desrosj desrosj commented Feb 23, 2026

This PR attempts to remove the requirement to checkout the WordPress/gutenberg Git repository locally to run the gutenberg repository build script as a part of the wordpress-develop one.

Instead a copy of the built plugin is downloaded from the GitHub Container Registry for the respective pinned hash value (see WordPress/gutenberg#75844 for that part of this approach). This is now the default behavior.

There is also overlap in preexisting Grunt tasks and what the related scripts currently do. This moves that behavior into Grunt to eliminate unnecessary custom code (mainly copy and replace commands).

Other changes in this PR:

  • Usage of ref to sha to avoid confusion. Ref is typically a human-readable branch path. A commit hash or SHA value is the full length, unique hash for a commit.
  • Gutenberg-related grunt tasks have been renamed to follow gutenberg:command instead of gutenberg-command pattern.
  • gutenberg has been removed from all file names within tools/gutenberg since it's redundant.
  • A single job has been added to the PHPUnit and build process testing workflows to confirm that both ways of building work as expected.

Trac ticket: Core-64393.

Use of AI Tools

I used Claude Code pretty heavily throughout the work on this pull request.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@desrosj desrosj self-assigned this Feb 23, 2026
@desrosj desrosj force-pushed the try/remove-gutenberg-git-checkout-and-build branch from edf4224 to 2e3f506 Compare February 23, 2026 20:47
@desrosj desrosj changed the title Proof of concept: Stop checking out gutenberg repository and running two build scripts Proof of concept: Remove the requirement to check out the gutenberg repository and running the full build script Feb 24, 2026
@desrosj
Copy link
Member Author

desrosj commented Feb 24, 2026

There is one remaining issue to figure out: how to map the icons package correctly from the built plugin.

I've removed the chunk of code responsible for mapping those files to their respective locations within wordpress-develop and the build process succeeds.

@desrosj
Copy link
Member Author

desrosj commented Feb 24, 2026

It seems that the icons are not currently included in the Gutenberg plugin, only in wordpress-develop.

@mcsf I see you introduced this in 5b7a3ad. Could you share a bit more context as to why these are not yet included in the Gutenberg plugin? I don't see them in the latest plugin version, but I may be missing them.

Copy link
Member

@dmsnell dmsnell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a step up @desrosj and should avoid a lot of the delay when checking out the repo. 🙇‍♂️

does the zip contain the proper built versions of the artifacts or does it contain source copies? that is, are there any build steps that are occurring on the Gutenberg side that occur before that ZIP is available?

it would definitely be helpful to know in the docs, at least in download-gutenberg.js because I think it’s really not clear if we are downloading the Gutenberg source or some output. if output, I would love to know on the wordpress-develop side what our expectations are of that artifact and why .layers[0].digest is special.

Perhaps there is a command we could include which comparably would generate the right files were we given the Gutenberg source.


is it right that this still leaves the icon disparity in place?

fs.readFileSync( packageJsonPath, 'utf8' )
);
sha = packageJson.gutenberg?.sha;
ghcrRepo = packageJson.gutenberg?.ghcrRepo;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we short on bytes? the gh part is obvious to me as a reader, but I cannot figure out what cr means, and since this only appears to be documented inside this file, I’m not sure it would be clear from the package.json file, especially since it looks like a standard GitHub repo name.

perhaps something githubRepo would suffice? it doesn’t seem like the package.json value has anything specific to do with the Github Container Registry

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am new to this as well, so I found that GHCR stands for the GitHub Container Registry

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a note within the inlind docblock to explain this at the first occurrence of ghcrRepo. Does that address your concerns here @dmsnell?

I also changed the name of the package to gutenberg-wp-develop-build, making the full reference WordPress/gutenberg/gutenberg-wp-develop-build. It's more specific to the purpose, and hopefully won't lead to any confusion.

Within the GHCR, packages are published to a specific repository. While they are listed on a separate screen, you need to use the Org/Repository/package path to reference them.

@mcsf
Copy link
Contributor

mcsf commented Feb 24, 2026

It seems that the icons are not currently included in the Gutenberg plugin, only in wordpress-develop.

@mcsf I see you introduced this in 5b7a3ad. Could you share a bit more context as to why these are not yet included in the Gutenberg plugin? I don't see them in the latest plugin version, but I may be missing them.

Oof, I'm so glad you pinged me! Fix at WordPress/gutenberg#75866

@github-actions
Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

throw new Error( `Failed to download blob: ${ response.status } ${ response.statusText }` );
}
const buffer = await response.arrayBuffer();
fs.writeFileSync( zipPath, Buffer.from( buffer ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at this point it’s probably possible to remove all of the _Sync versions and stream the output directly into the file.

not essential, but it does feel like we’re possibly going out of our way to do more work to avoid a simpler default.

await fs.writeFile( zipPath, response.body );

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not deeply knowledgeable here, but Claude told me that this wouldn't work as you've written "since fs.promises.writeFile doesn't accept a ReadableStream — stream.pipeline is the correct API for this." Does this seem right now?

@desrosj
Copy link
Member Author

desrosj commented Feb 28, 2026

I still need to go through and polish this, but the PR for the Gutenberg repository is ready. That should be merged first anyway, and these changes can't be committed until after the first hash update following that PR being merged. So I'm hoping to clean this up on Monday.

Copy link
Member

@dmsnell dmsnell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@desrosj please don’t let my comments discourage you; this is really good stuff.

did you by any chance try to measure the time it takes to run the dir hashing? I would think that could have some unexpected lag to it.

@desrosj
Copy link
Member Author

desrosj commented Mar 3, 2026

did you by any chance try to measure the time it takes to run the dir hashing? I would think that could have some unexpected lag to it.

My initial rough measurements were showing that the hashing takes around 150-200ms with the downloaded pre-built plugin files, and ~2.5 seconds with the full repository checkout.

response.body,
zlib.createGunzip(),
Writable.toWeb( tar.stdin ),
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah darn, sorry if I leaned too hard into this and we still have to call out to the shell.

desrosj added 5 commits March 5, 2026 20:10
This switches to downloading a prebuilt zip file uploaded to the GitHub Container Registry for a given commit instead.

This eliminates the need to run any Gutenberg build scripts within `wordpress-develop`.
A REF is typically a human-readable path to a branch where as a SHA is a hash representing an individual commit. Since the latter is what's being used here, this aims to avoid confusion.
desrosj added 7 commits March 5, 2026 20:23
It's redundant to include `gutenberg` in the file names.
This has better native support in Node.
This removes some unused code and variables, updates some comments to be more accurate, and ensures all one line inline comments end with a period.
@desrosj desrosj force-pushed the try/remove-gutenberg-git-checkout-and-build branch from 50e571e to 50c6285 Compare March 6, 2026 02:55
@desrosj
Copy link
Member Author

desrosj commented Mar 6, 2026

I talked through this PR with @dmsnell today, and we arrived at the decision that it was best to leave all aspects of using the git repository out for this iteration.

While it's possible someone would could be using the gutenberg directory to test changes to the wp-build package, the script currently only performs a git clone at a --depth=1, which is not particularly useful for any meaningful contributions. We can always add this back later if it turns out this is an actual flow contributors will want to use.

We also decided that for the time being, there's no need to hash the entire gutenberg directory to try and detect changes to the files. If the files are re-added to version control, any unexpected changes to them would appear after running build anyway.

I've gone and rebased all of the changes onto the latest from trunk and removed all of the commits that re-added the Git-related functionality. I think this is good to get reviewed and tested.

There are a few pre-existing grunt tasks that are performing identical tasks to some of the code in `copy.js`. Mainly:
- `grunt-contrib-copy` copies files with the ability to perform custom transformations.
- `grunt-contrib-replace` replaces strings and pattern matches, which is primarily used to remove `sourceMappingURL` from JavaScript files.
@desrosj desrosj marked this pull request as ready for review March 6, 2026 05:13
@github-actions
Copy link

github-actions bot commented Mar 6, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props desrosj, dmsnell, westonruter, mcsf.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@desrosj
Copy link
Member Author

desrosj commented Mar 6, 2026

I also realized that a good portion of the custom code in the tools/gutenberg is performing tasks that are already handled within grunt.

I've made adjustments so that as much as possible (where other complex modifications are not being made), the Gutenberg-related files are now copied by grunt-contrib-copy instead. Also, grunt-contrib-replace already was replacing sourceMappingURL in ~50 files. It now replaces the pattern for all Gutenberg related files that are also moved into place in the new build script.

@desrosj desrosj requested review from aaronjorbin, ellatrix, peterwilsoncc and youknowriad and removed request for youknowriad March 6, 2026 05:16
// Fix boot module asset file path for Core's different directory structure.
return content.replace(
/__DIR__\s*\.\s*['"]\/..\/\..\/modules\/boot\/index\.min\.asset\.php['"]/g,
'ABSPATH . WPINC . \'/js/dist/script-modules/boot/index.min.asset.php\''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'ABSPATH . WPINC . \'/js/dist/script-modules/boot/index.min.asset.php\''
"ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'"

process: function( content ) {
// Fix boot module asset file path for Core's different directory structure.
return content.replace(
/__DIR__\s*\.\s*['"]\/..\/\..\/modules\/boot\/index\.min\.asset\.php['"]/g,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/__DIR__\s*\.\s*['"]\/..\/\..\/modules\/boot\/index\.min\.asset\.php['"]/g,
/__DIR__\s*\.\s*(['"])\/..\/\..\/modules\/boot\/index\.min\.asset\.php\1/g,

minor nit, and unlikely to cause any trouble, but when we match the opening quote character we can make sure we match the terminating quote of the same kind.

problems should already be evident within the PHP if we have something like this, and an over-match here with this pattern would/should do no harm, but it’s not terrible to match assertively here on matching quotes.

// this matches ['"]...['"] but not ['"]...\1
require __DIR__ . '/../../modules/boot/index.min.asset.php";

'"$schema": "../schemas/json/theme.json"',
'"$schema": "https://schemas.wp.org/trunk/theme.json"'
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does in a string things that are likely to mis-parse or be mislead. again, there should be extra layers of protection here, however, it is not likely that much more difficult to do this in a more robust way.

process: function( content, srcpath ) {
	if ( path.basename( srcpath ) !== 'theme.json' ) {
		return content;
	}

	return JSON.stringify(
		JSON.parse( content ),
		( key, value ) =>
			( '$schema' === key && '../schemas/json/theme.json' === value )
				? 'https://schemas.wp.org/trunk/theme.json'
				: value,
		2
	);
}

this is marginally larger but contains none of the issues with treating JSON as a string. it’s not a big deal here, so not a blocker in any way, but it would be nice if we could at least follow-up after fixing things to return to these low-level build functions and make sure they don’t contain foot-guns for handling well-specified data.

},
{
src: 'gutenberg/lib/theme-i18n.json',
dest: WORKING_DIR + 'wp-includes/theme-i18n.json',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while it looks like theme-i18n.json contains no $schema property, is there a reason to explicitly avoid changing the relative path to an absolute one? it seems somewhat odd given that this list of files includes two files and the process function only operates on one of them.

not really a block, but something perhaps to earmark.

'_x( $1, $2 )'
);
}
return content;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here again we’re apparently adding automation which nobody is likely to be maintaining familiarity with, doing something that looks sound today when all of the files are the way we expect them, and then one day it will all break and nobody will know what the goal here is. we’re also doing naive string parsing of JSON files again.

it seems like Gutenberg’s packages/icons/lib/generate-manifest-php.cjs is building the manifest.php from the JSON source anyway, so I wonder if we could parameterize that method with the intended text domain and simply regenerate this file.

should the shape of the manifest change, I have no idea how to expect someone to know what the original goal of this RegExp was supposed to be or do.

this is definitely a nice-to-have, particularly since the generator file should be available whenever we have the manifest.php file, but also potentially something to address in a follow-up.

I would think we could directly call the generator, like return iconPhpManifestGenerator.generateManifestPHP( null ) as long as we add the text domain parameters where appropriate

// Run main function.
main().catch( ( error ) => {
console.error( '❌ Unexpected error:', error );
process.exit( 1 );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started working through this file locally because I was finding it hard to review in GitHub, and it was hard to review on its own. lots of unclear logic, clearly fragile code, duplication, misleading names and comments, and hard-coded ad-hoc details 🤦‍♂️

would be nice to think about replacing this file entirely, but after 7.0.

thank you for all your hard work nudging through this.

one thing I did find is that this has hard-coded logic for the vips library, which is really surprising to me, buried in the middle of this large process. I know there was some concern about VIPS loading 3.5 MB of compressed JS (which expands to 10 MB of JS in the browser). if people do end up modifying what we do with client-side image manipulation I find it hard to imagine they would find this code here to update it accordingly.

@dmsnell
Copy link
Member

dmsnell commented Mar 7, 2026

@desrosj in 3050cd1 I replaced the string/RegExp-based PHP parser with php, then in f588cf5 I moved it up near the top of the file so GitHub’s diff viewer wouldn’t show it interspersed with the now-removed parsePHPArray(). this new version could be slower due to the startup time involved in calling php in a new process, but I think that will be paid for in not having to maintain that code, and the code was already full of latent parsing issues.

as I’ve reviewed this today I keep finding places we’re doing the lots of repetitive work which doesn’t even need to be done, but it’s scattered through the files. for example, a lot of these PHP files are generated in Gutenberg from JSON, and then parsed as string in here in wordpress-develop, and then JSON-stringified, and then JSON-parsed. the whole thing is kind of mind boggling and I think we could probably eliminate a lot more code just by doing const data = require( 'manifest.json' ) instead of what’s happening now.

so I apologize, but this last statement isn’t fully actionable; just a little venting and one contributing factor to why it’s taken me so long to review this or the original change that introduced the code modified here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants