chore: improve gh-pages publishing steps (#9604)

* fix: Fix gulpfiles related to publishing GitHub pages

* chore: remove unused gulp tasks

* feat: allow passing a remote to push gh-pages to

* feat: add ability to skip syncing with main

* feat: add gh workflow to publish ghpages

* chore: update node version

---------

Co-authored-by: Aaron Dodson <aaron.dodson@raspberrypi.org>
This commit is contained in:
Maribeth Moffatt
2026-03-03 15:26:01 -05:00
committed by GitHub
parent a16580ae26
commit 3caee2faf5
5 changed files with 153 additions and 195 deletions
+36
View File
@@ -0,0 +1,36 @@
# Manual workflow to update GitHub Pages from a chosen source branch.
# The gulp updateGithubPages task builds the repo and force-pushes to gh-pages.
name: Update GitHub Pages
on:
workflow_dispatch:
inputs:
source_branch:
description: 'Source branch to build and deploy to GitHub Pages'
required: true
type: string
default: main
permissions:
contents: write
jobs:
update-gh-pages:
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ inputs.source_branch }}
fetch-depth: 0
- name: Use Node.js
uses: actions/setup-node@v5
with:
node-version: 24.x
- name: Update GitHub Pages
working-directory: ./packages/blockly
run: npm run updateGithubPages:staging
+3 -22
View File
@@ -33,18 +33,9 @@ import {
tsc,
} from './scripts/gulpfiles/build_tasks.mjs';
import {docs} from './scripts/gulpfiles/docs_tasks.mjs';
import {
createRC,
syncDevelop,
syncMaster,
updateGithubPages,
} from './scripts/gulpfiles/git_tasks.mjs';
import {updateGithubPages} from './scripts/gulpfiles/git_tasks.mjs';
import {cleanReleaseDir, pack} from './scripts/gulpfiles/package_tasks.mjs';
import {
publish,
publishBeta,
recompile,
} from './scripts/gulpfiles/release_tasks.mjs';
import {publish, publishBeta} from './scripts/gulpfiles/release_tasks.mjs';
import {
generators,
interactiveMocha,
@@ -72,7 +63,7 @@ export {
prepareDemos,
deployDemosBeta,
deployDemos,
updateGithubPages as gitUpdateGithubPages,
updateGithubPages,
}
// Manually-invokable targets that also invoke prerequisites where
@@ -86,15 +77,5 @@ export {
generators as testGenerators,
interactiveMocha,
buildAdvancedCompilationTest,
createRC as gitCreateRC,
docs,
}
// Legacy targets, to be deleted.
//
// prettier-ignore
export {
recompile,
syncDevelop as gitSyncDevelop,
syncMaster as gitSyncMaster,
}
+2 -3
View File
@@ -38,8 +38,6 @@
"prepareDemos": "gulp prepareDemos",
"publish": "npm ci && gulp publish",
"publish:beta": "npm ci && gulp publishBeta",
"recompile": "gulp recompile",
"release": "gulp gitCreateRC",
"start": "npm run build && concurrently -n tsc,server \"tsc --watch --preserveWatchOutput --outDir \"build/src\" --declarationDir \"build/declarations\"\" \"http-server ./ -s -o /tests/playground.html -c-1\"",
"tsc": "gulp tsc",
"test": "gulp test",
@@ -47,7 +45,8 @@
"test:generators": "gulp testGenerators",
"test:mocha:interactive": "npm run build && concurrently -n tsc,server \"tsc --watch --preserveWatchOutput --outDir \"build/src\" --declarationDir \"build/declarations\"\" \"gulp interactiveMocha\"",
"test:compile:advanced": "gulp buildAdvancedCompilationTest --debug",
"updateGithubPages": "npm ci && gulp gitUpdateGithubPages"
"updateGithubPages": "npm ci && gulp updateGithubPages --upstream",
"updateGithubPages:staging": "npm ci && gulp updateGithubPages --use-local"
},
"exports": {
".": {
+112 -111
View File
@@ -8,17 +8,36 @@
* @fileoverview Git-related gulp tasks for Blockly.
*/
import * as gulp from 'gulp';
import {execSync} from 'child_process';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import * as buildTasks from './build_tasks.mjs';
import * as packageTasks from './package_tasks.mjs';
const UPSTREAM_URL = 'https://github.com/google/blockly.git';
const UPSTREAM_URL = 'git@github.com:RaspberryPiFoundation/blockly.git';
// Use yargs to parse --remote argument
const argv = yargs(hideBin(process.argv)).option('remote', {
type: 'string',
describe: 'Remote to push gh-pages to',
demandOption: false
}).option('upstream', {
type: 'boolean',
describe: 'Push to RaspberryPiFoundation/blockly instead of origin',
demandOption: false
}).option('use-local', {
type: 'boolean',
describe: 'Build and push from current branch instead of syncing with main',
demandOption: false
}).help().argv;
const remoteToUse = argv.upstream ? UPSTREAM_URL : resolveRemote(argv.remote);
/**
* Extra paths to include in the gh_pages branch (beyond the normal
* contents of master / develop). Passed to shell unquoted, so can
* contents of main). Passed to shell unquoted, so can
* include globs.
*/
const EXTRAS = [
@@ -28,140 +47,122 @@ const EXTRAS = [
'build/*.loader.mjs',
];
let upstream = null;
/**
* Get name of git remote for upstream (typically 'upstream', but this
* is just convention and can be changed.)
*/
function getUpstream() {
if (upstream) return upstream;
for (const line of String(execSync('git remote -v')).split('\n')) {
if (line.includes('google/blockly')) {
upstream = line.split('\t')[0];
return upstream;
}
}
throw new Error('Unable to determine upstream URL');
}
/**
* Stash current state, check out the named branch, and sync with
* google/blockly.
* Stash current state, check out the named branch, and pull
* changes from RaspberryPiFoundation/blockly.
*/
function syncBranch(branchName) {
return function(done) {
execSync('git stash save -m "Stash for sync"', { stdio: 'inherit' });
checkoutBranch(branchName);
execSync(`git pull ${UPSTREAM_URL} ${branchName}`, { stdio: 'inherit' });
execSync(`git push origin ${branchName}`, { stdio: 'inherit' });
done();
};
}
/**
* Stash current state, check out develop, and sync with
* google/blockly.
* Stash current state, check out main, and sync with
* RaspberryPiFoundation/blockly.
*/
export function syncDevelop() {
return syncBranch('develop');
export function syncMain() {
return syncBranch('main');
};
/**
* Stash current state, check out master, and sync with
* google/blockly.
*/
export function syncMaster() {
return syncBranch('master');
};
/**
* Helper function: get a name for a rebuild branch. Format:
* rebuild_mm_dd_yyyy.
*/
function getRebuildBranchName() {
const date = new Date();
const mm = date.getMonth() + 1; // Month, 0-11
const dd = date.getDate(); // Day of the month, 1-31
const yyyy = date.getFullYear();
return `rebuild_${mm}_${dd}_${yyyy}`;
};
/**
* Helper function: get a name for a rebuild branch. Format:
* rebuild_yyyy_mm.
*/
function getRCBranchName() {
const date = new Date();
const mm = date.getMonth() + 1; // Month, 0-11
const yyyy = date.getFullYear();
return `rc_${yyyy}_${mm}`;
};
/**
* If branch does not exist then create the branch.
* If branch exists switch to branch.
* If branch does not exist then create the branch.
*/
function checkoutBranch(branchName) {
execSync(`git switch -c ${branchName}`,
execSync(`git switch ${branchName} || git switch -c ${branchName}`,
{ stdio: 'inherit' });
}
/**
* Create and push an RC branch.
* Note that this pushes to google/blockly.
*/
export const createRC = gulp.series(
syncDevelop(),
function(done) {
const branchName = getRCBranchName();
execSync(`git switch -C ${branchName}`, { stdio: 'inherit' });
execSync(`git push ${UPSTREAM_URL} ${branchName}`, { stdio: 'inherit' });
done();
}
);
/** Create the rebuild branch. */
export function createRebuildBranch(done) {
const branchName = getRebuildBranchName();
console.log(`make-rebuild-branch: creating branch ${branchName}`);
execSync(`git switch -C ${branchName}`, { stdio: 'inherit' });
done();
}
/** Push the rebuild branch to origin. */
export function pushRebuildBranch(done) {
console.log('push-rebuild-branch: committing rebuild');
execSync('git commit -am "Rebuild"', { stdio: 'inherit' });
const branchName = getRebuildBranchName();
execSync(`git push origin ${branchName}`, { stdio: 'inherit' });
console.log(`Branch ${branchName} pushed to GitHub.`);
console.log('Next step: create a pull request against develop.');
done();
}
/**
* Update github pages with what is currently in develop.
* Update github pages with what is currently in main (or current branch if --use-local).
*
* Prerequisites (invoked): clean, build.
*
* Usage:
* gulp updateGithubPages # sync main, then use origin if exists
* gulp updateGithubPages --upstream # uses hardcoded upstream
* gulp updateGithubPages --remote <remote> # uses named remote
* gulp updateGithubPages --use-local # build from current branch, skip syncing main
*
*/
export const updateGithubPages = gulp.series(
function(done) {
execSync('git stash save -m "Stash for sync"', { stdio: 'inherit' });
execSync('git switch -C gh-pages', { stdio: 'inherit' });
execSync(`git fetch ${getUpstream()}`, { stdio: 'inherit' });
execSync(`git reset --hard ${getUpstream()}/develop`, { stdio: 'inherit' });
done();
},
buildTasks.cleanBuildDir,
packageTasks.cleanReleaseDir,
buildTasks.build,
function(done) {
// Extra paths (e.g. build/, dist/ etc.) are normally gitignored,
// so we have to force add.
execSync(`git add -f ${EXTRAS.join(' ')}`, {stdio: 'inherit'});
execSync('git commit -am "Rebuild"', {stdio: 'inherit'});
execSync(`git push ${UPSTREAM_URL} gh-pages --force`, {stdio: 'inherit'});
done();
function (done) {
if (!remoteToUse) {
const attemptedRemote = argv.remote || 'origin';
const remoteLabel = argv.remote
? `Remote '${attemptedRemote}'`
: "Remote 'origin' (default)";
const errMsg = `${remoteLabel} not found in git remotes. ` +
'Please add that remote or use --upstream.\n' +
'Usage: gulp updateGithubPages [--remote <remote> | --upstream]';
console.error(errMsg);
done(new Error(errMsg));
return;
}
done();
},
function (done) {
if (!argv.useLocal) {
done();
return;
}
const status = execSync('git status --porcelain', { encoding: 'utf8' });
if (status.trim()) {
const errMsg =
'You cannot push the local branch with uncommitted changes. ' +
'Please commit or stash your changes first.';
console.error(errMsg);
done(new Error(errMsg));
return;
}
done();
},
function (done) {
if (argv.useLocal) {
done();
return;
}
syncMain()(done);
},
function(done) {
const sourceRef = argv.useLocal
? execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim()
: 'main';
execSync('git switch -C gh-pages', { stdio: 'inherit' });
execSync(`git reset --hard ${sourceRef}`, { stdio: 'inherit' });
done();
},
buildTasks.cleanBuildDir,
packageTasks.cleanReleaseDir,
buildTasks.build,
function(done) {
// Extra paths (e.g. build/, dist/ etc.) are normally gitignored,
// so we have to force add.
execSync(`git add -f ${EXTRAS.join(' ')}`, {stdio: 'inherit'});
execSync('git commit -am "Rebuild"', {stdio: 'inherit'});
execSync(`git push ${remoteToUse} gh-pages --force`, {stdio: 'inherit'});
done();
}
);
/**
* Resolves which remote to use for pushing gh-pages.
* @param {string} remoteArg
* @returns {string|undefined} The remote name, or undefined if not found.
*/
function resolveRemote(remoteArg) {
const remoteName = remoteArg || 'origin';
try {
const remotes = execSync('git remote', {encoding: 'utf8'}).split(/\r?\n/).map(r => r.trim()).filter(Boolean);
if (remotes.includes(remoteName)) {
return remoteName;
}
return undefined;
} catch (e) {
return undefined;
}
);
}
@@ -18,55 +18,6 @@ import * as packageTasks from './package_tasks.mjs';
import {getPackageJson} from './helper_tasks.mjs';
import {RELEASE_DIR} from './config.mjs';
// Gets the current major version.
function getMajorVersion() {
const { version } = getPackageJson();
const re = new RegExp(/^(\d)./);
const match = re.exec(version);
if (!match[0]) {
return null;
}
console.log(match[0]);
return parseInt(match[0]);
}
// Updates the version depending on user input.
function updateVersion(done, updateType) {
const majorVersion = getMajorVersion();
if (!majorVersion) {
done(new Error('Something went wrong when getting the major version number.'));
} else if (!updateType) {
// User selected to cancel.
done(new Error('Cancelling process.'));
}
switch (updateType.toLowerCase()) {
case 'major':
majorVersion++;
execSync(`npm --no-git-tag-version version ${majorVersion}.$(date +'%Y%m%d').0`, {stdio: 'inherit'});
done();
break;
case 'minor':
execSync(`npm --no-git-tag-version version ${majorVersion}.$(date +'%Y%m%d').0`, {stdio: 'inherit'});
done();
break;
case 'patch':
execSync(`npm --no-git-tag-version version patch`, {stdio: 'inherit'});
done();
break;
default:
done(new Error('Unexpected update type was chosen.'))
}
}
// Prompt the user to figure out what kind of version update we should do.
function updateVersionPrompt(done) {
const releaseTypes = ['Major', 'Minor', 'Patch'];
const index = readlineSync.keyInSelect(releaseTypes, 'Which version type?');
updateVersion(done, releaseTypes[index]);
}
// Checks with the user that they are on the correct git branch.
function checkBranch(done) {
const gitBranchName = execSync('git rev-parse --abbrev-ref HEAD').toString();
@@ -162,13 +113,3 @@ export const publishBeta = gulp.series(
checkReleaseDir,
loginAndPublishBeta
);
// Switch to a new branch, update the version number, build Blockly
// and check in the resulting built files.
export const recompile = gulp.series(
gitTasks.syncDevelop(),
gitTasks.createRebuildBranch,
updateVersionPrompt,
packageTasks.pack, // Does clean + build.
gitTasks.pushRebuildBranch
);