diff --git a/.github/workflows/update-gh-pages.yml b/.github/workflows/update-gh-pages.yml new file mode 100644 index 00000000000..aa19a1b8b55 --- /dev/null +++ b/.github/workflows/update-gh-pages.yml @@ -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: 20.x + + - name: Update GitHub Pages + working-directory: ./packages/blockly + run: npm run updateGithubPages:staging diff --git a/packages/blockly/gulpfile.mjs b/packages/blockly/gulpfile.mjs index ad61bcb516d..378d6ce5efa 100644 --- a/packages/blockly/gulpfile.mjs +++ b/packages/blockly/gulpfile.mjs @@ -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, -} diff --git a/packages/blockly/package.json b/packages/blockly/package.json index 450fd302a71..ffd12bd76d3 100644 --- a/packages/blockly/package.json +++ b/packages/blockly/package.json @@ -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": { ".": { diff --git a/packages/blockly/scripts/gulpfiles/git_tasks.mjs b/packages/blockly/scripts/gulpfiles/git_tasks.mjs index 2b08e16b38b..6a68b54becf 100644 --- a/packages/blockly/scripts/gulpfiles/git_tasks.mjs +++ b/packages/blockly/scripts/gulpfiles/git_tasks.mjs @@ -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 # 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 | --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; } -); +} diff --git a/packages/blockly/scripts/gulpfiles/release_tasks.mjs b/packages/blockly/scripts/gulpfiles/release_tasks.mjs index a678a4f2436..ca9c9ffa1bc 100644 --- a/packages/blockly/scripts/gulpfiles/release_tasks.mjs +++ b/packages/blockly/scripts/gulpfiles/release_tasks.mjs @@ -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 - );