diff --git a/.husky/post-merge b/.husky/post-merge new file mode 100755 index 00000000000..a9157b5f60d --- /dev/null +++ b/.husky/post-merge @@ -0,0 +1,15 @@ +#!/bin/sh +# Generated with husky-init and https://github.com/typicode/husky-4-to-6 +. "$(dirname "$0")/_/husky.sh" + +# ATLASSIAN: Update self-published (nightly) versions as part of a merge. +# Ideally, this would be done as part of a `pre-merge-commit` hook, +# but git does not support modifying the index during a merge commit. +# So, we hack it in this `post-merge` hook by immediately ammending the commit. +# Note that in cases where the merge commit did not automatically succeed, +# this hook will not be run. See comments in `pre-commit` hook for more. +yarn update-self-published -a +if [[ -f .git/MERGE_HEAD ]]; then + rm .git/MERGE_HEAD +fi +git log -n 1 --pretty=tformat:%s%n%n%b | git commit -F - --amend diff --git a/.husky/pre-commit b/.husky/pre-commit index 5ec2f9970aa..3f301f9a53c 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -3,3 +3,14 @@ . "$(dirname "$0")/_/husky.sh" yarn lint-staged + +# ATLASSIAN: Update self-published (nightly) versions as part of a merge. +# If a merge is committed automatically (e.g., no conflicts), this hook +# will not be run (see comments in `post-merge` for more). However, +# if a merge is not committed automatically (e.g., there are conflicts), +# the `post-merge` hook will not be run. Here we detect that a merge +# is being manually committed and replicate the work our `post-merge` hook +# would have done if the merge commit had proceeded automatically. +if git rev-parse -q --verify MERGE_HEAD >/dev/null 2>&1; then + yarn update-self-published -a +fi diff --git a/package.json b/package.json index 7361104e92b..46b176c528e 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "test:integration-ci": "yarn workspace @parcel/integration-tests test-ci", "test": "yarn test:unit && yarn test:integration", "update-readme-toc": "doctoc README.md", + "update-self-published": "node scripts/update-self-published.js", "version:atlassian": "yarn lerna version patch --exact --force-publish=@atlassian/internal-parcel-utils,@atlassian/parcel-reporter-analytics", "nightly:release": "lerna publish -y --canary --preid nightly --dist-tag=nightly --exact --force-publish=* --no-git-tag-version --no-push", "tag:prerelease": "lerna version --exact --force-publish=* --no-git-tag-version --no-push && yarn adjust-versions --exact", diff --git a/packages/optimizers/image/package.json b/packages/optimizers/image/package.json index 1dd6de71290..2f5a20523b4 100644 --- a/packages/optimizers/image/package.json +++ b/packages/optimizers/image/package.json @@ -37,7 +37,7 @@ "@parcel/utils": "2.0.24", "@parcel/workers": "2.0.24", "detect-libc": "^1.0.3", - "self-published": "npm:@parcel/optimizer-image@2.5.1-nightly.2696" + "self-published": "npm:@parcel/optimizer-image@2.5.1-nightly.2700" }, "devDependencies": { "@napi-rs/cli": "^2.6.2", diff --git a/packages/transformers/js/package.json b/packages/transformers/js/package.json index 449e43083e9..bb798371bdf 100644 --- a/packages/transformers/js/package.json +++ b/packages/transformers/js/package.json @@ -40,7 +40,7 @@ "detect-libc": "^1.0.3", "nullthrows": "^1.1.1", "regenerator-runtime": "^0.13.7", - "self-published": "npm:@parcel/transformer-js@2.0.0-nightly.1073", + "self-published": "npm:@parcel/transformer-js@2.0.0-nightly.1077", "semver": "^5.7.1" }, "devDependencies": { diff --git a/packages/utils/fs-search/package.json b/packages/utils/fs-search/package.json index 74add031c97..b401d7c8703 100644 --- a/packages/utils/fs-search/package.json +++ b/packages/utils/fs-search/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "detect-libc": "^1.0.3", - "self-published": "npm:@parcel/fs-search@2.5.1-nightly.2696" + "self-published": "npm:@parcel/fs-search@2.5.1-nightly.2700" }, "devDependencies": { "@napi-rs/cli": "^2.6.2" diff --git a/packages/utils/hash/package.json b/packages/utils/hash/package.json index 6a7a3708cc9..012381e8af6 100644 --- a/packages/utils/hash/package.json +++ b/packages/utils/hash/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "detect-libc": "^1.0.3", - "self-published": "npm:@parcel/hash@2.5.1-nightly.2696", + "self-published": "npm:@parcel/hash@2.5.1-nightly.2700", "xxhash-wasm": "^0.4.2" }, "devDependencies": { diff --git a/scripts/update-self-published.js b/scripts/update-self-published.js new file mode 100755 index 00000000000..4a0cb938388 --- /dev/null +++ b/scripts/update-self-published.js @@ -0,0 +1,112 @@ +#!/usr/bin/env node + +/* eslint-disable no-console */ +const fs = require('fs'); +const path = require('path'); +const exec = require('child_process').execSync; + +const UPSTREAM = /github.+parcel-bundler\/parcel/; +const NIGHTLY = /.*-nightly\..*/; + +if (process.argv.includes('-h') || process.argv.includes('--help')) { + console.log( + [ + ` Usage: ${path.basename(process.argv[1])} [opts]`, + '', + ' Options:', + ' -h, --help Show help', + ' -a Add updated files to git index', + ' (handy in a precommit hook)', + '', + ' Looks for self-published packages (e.g., @parcel/transformer-js),', + ' and compares their nightly version numbers to the list', + ' of published nightly versions (via `yarn info`).', + ' If it finds a version that is newer, it updates the version', + ' in the package.json.', + '', + ' It will use the oldest nightly version that is newer than', + ' the latest common commit between HEAD and the upstream default branch.', + ].join('\n'), + ); + process.exit(); +} + +const shouldStage = process.argv.includes('-a'); + +const fromVersionString = str => str.match(/npm:.+@(.+)$/)[1]; +const toVersionString = (name, version) => `npm:${name}@${version}`; + +function run(cmd) { + let result = exec(cmd, {stdio: [0, 'pipe', 2]}); + try { + return JSON.parse(result); + } catch (e) { + if (e instanceof SyntaxError) { + return result.toString().trim(); + } else { + throw e; + } + } +} + +function getUpstreamRemoteName() { + for (let name of run(`git remote`).split(/\s+/)) { + if (UPSTREAM.test(run(`git remote get-url ${name}`))) { + return name; + } + } + throw new Error('Could not determine an upstream remote name!'); +} + +function getDefaultBranchName(remote) { + return run(`git rev-parse --abbrev-ref ${remote}`).split(`${remote}/`).pop(); +} + +/** + * Check nightly version timestamps for the given `pkgName` + * in reverse chronological order, returning the version + * with the timestamp closest to `time` without being younger. + */ +function getNearestNightlyVersion(pkgName, time) { + let candidate = null; + let info = run(`yarn info ${pkgName} --json`); + let versions = [...Object.entries(info.data.time)].reverse(); + for (let [version, timestamp] of versions) { + if (NIGHTLY.test(version)) { + let versionTime = new Date(timestamp); + if (versionTime < time) break; + candidate = version; + } + } + return candidate; +} + +console.log(`Updating self-published (nightly) versions...`); + +let packages = run( + `${path.join(__dirname, '..', 'node_modules', '.bin', 'lerna')} ls --json`, +); + +// Fetch the default upstream branch... +let upstream = getUpstreamRemoteName(); +let branch = getDefaultBranchName(upstream); +run(`git fetch -q ${upstream} ${branch}`); +// ...so we can determine the latest common ancestor commit. +let baseRef = run(`git merge-base HEAD ${upstream}/${branch}`).split(/\s+/)[0]; +// Get the commit time of the latest common ancestor between HEAD and upstream. +let baseRefTime = new Date(run(`git show -s --format=%cI ${baseRef}`)); + +for (let {location, name} of packages) { + let pkgPath = path.join(location, 'package.json'); + let pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + if (pkg.dependencies && 'self-published' in pkg.dependencies) { + let current = fromVersionString(pkg.dependencies['self-published']); + let nightly = getNearestNightlyVersion(name, baseRefTime); + if (nightly && current !== nightly) { + console.log(`updating ${name} to nearest nightly ${nightly}`); + pkg.dependencies['self-published'] = toVersionString(name, nightly); + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); + if (shouldStage) run(`git add -u ${pkgPath}`); + } + } +}