Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/update-gh-pages.yml
Original file line number Diff line number Diff line change
@@ -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
25 changes: 3 additions & 22 deletions packages/blockly/gulpfile.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -72,7 +63,7 @@ export {
prepareDemos,
deployDemosBeta,
deployDemos,
updateGithubPages as gitUpdateGithubPages,
updateGithubPages,
}

// Manually-invokable targets that also invoke prerequisites where
Expand All @@ -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,
}
5 changes: 2 additions & 3 deletions packages/blockly/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,15 @@
"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",
"test:browser": "npx mocha --config tests/browser/.mocharc.js",
"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": {
".": {
Expand Down
223 changes: 112 additions & 111 deletions packages/blockly/scripts/gulpfiles/git_tasks.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -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;
}
);
}
Loading
Loading