Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support yarn v2+ w/wo corepack enabled #507

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
31 changes: 22 additions & 9 deletions src/getPackageResolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ export function getPackageResolution({
throw new Error("Can't find yarn.lock file")
}
const lockFileString = readFileSync(lockFilePath).toString()
let appLockFile
let appLockFile: Record<
string,
{
version: string
resolution?: string
resolved?: string
}
>
if (lockFileString.includes("yarn lockfile v1")) {
const parsedYarnLockFile = parseYarnLockFile(lockFileString)
if (parsedYarnLockFile.type !== "success") {
Expand Down Expand Up @@ -59,7 +66,6 @@ export function getPackageResolution({
)

const resolutions = entries.map(([_, v]) => {
// @ts-ignore
return v.resolved
})

Expand All @@ -71,7 +77,7 @@ export function getPackageResolution({

if (new Set(resolutions).size !== 1) {
console.log(
`Ambigious lockfile entries for ${packageDetails.pathSpecifier}. Using version ${installedVersion}`,
`Ambiguous lockfile entries for ${packageDetails.pathSpecifier}. Using version ${installedVersion}`,
)
return installedVersion
}
Expand All @@ -80,18 +86,25 @@ export function getPackageResolution({
return resolutions[0]
}

const resolution = entries[0][0].slice(packageDetails.name.length + 1)
const packageName = packageDetails.name

const resolutionVersion = entries[0][1].version

// `@backstage/integration@npm:^1.5.0, @backstage/integration@npm:^1.7.0, @backstage/integration@npm:^1.7.2`
// ->
// `^1.5.0 ^1.7.0 ^1.7.2`
const resolution = entries[0][0]
.replace(new RegExp(packageName + "@", "g"), "")
.replace(/npm:/g, "")
.replace(/,/g, "")

// resolve relative file path
if (resolution.startsWith("file:.")) {
return `file:${resolve(appPath, resolution.slice("file:".length))}`
}

if (resolution.startsWith("npm:")) {
return resolution.replace("npm:", "")
}

return resolution
// add `resolutionVersion` to ensure correct version, `^1.0.0` could resolve latest `v1.3.0`, but `^1.0.0 1.2.1` won't
return resolutionVersion ? resolution + " " + resolutionVersion : resolution
} else {
const lockfile = require(join(
appPath,
Expand Down
38 changes: 30 additions & 8 deletions src/makePatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ export function makePatch({
writeFileSync(
tmpRepoPackageJsonPath,
JSON.stringify({
// support `corepack` enabled without `.yarn/releases`
packageManager: appPackageJson.packageManager,
dependencies: {
[packageDetails.name]: getPackageResolution({
packageDetails,
Expand All @@ -193,7 +195,14 @@ export function makePatch({
// copy .npmrc/.yarnrc in case packages are hosted in private registry
// copy .yarn directory as well to ensure installations work in yarn 2
// tslint:disable-next-line:align
;[".npmrc", ".yarnrc", ".yarn"].forEach((rcFile) => {
;[
".npmrc",
".yarnrc",
".yarnrc.yml",
// don't include the whole `.yarn` directory which could contain huge `cache`
".yarn/plugins",
".yarn/releases",
].forEach((rcFile) => {
const rcPath = join(appPath, rcFile)
if (existsSync(rcPath)) {
copySync(rcPath, join(tmpRepo.name, rcFile), { dereference: true })
Expand All @@ -205,10 +214,19 @@ export function makePatch({
chalk.grey("•"),
`Installing ${packageDetails.name}@${packageVersion} with yarn`,
)
const yarnArgs = ["install"]
const yarnVersionCmd = spawnSafeSync(`yarn`, ["--version"], {
cwd: tmpRepoNpmRoot,
logStdErrOnError: false,
})
const isYarnV1 = yarnVersionCmd.stdout.toString().startsWith("1.")
if (isYarnV1) {
yarnArgs.push("--ignore-engines")
}
try {
// try first without ignoring scripts in case they are required
// this works in 99.99% of cases
spawnSafeSync(`yarn`, ["install", "--ignore-engines"], {
spawnSafeSync(`yarn`, yarnArgs, {
cwd: tmpRepoNpmRoot,
logStdErrOnError: false,
})
Expand All @@ -217,7 +235,7 @@ export function makePatch({
// an implicit context which we haven't reproduced
spawnSafeSync(
`yarn`,
["install", "--ignore-engines", "--ignore-scripts"],
[...yarnArgs, isYarnV1 ? "--ignore-scripts" : "--mode=skip-build"],
{
cwd: tmpRepoNpmRoot,
},
Expand Down Expand Up @@ -338,9 +356,8 @@ export function makePatch({
try {
parsePatchFile(diffResult.stdout.toString())
} catch (e) {
if (
(e as Error).message.includes("Unexpected file mode string: 120000")
) {
const err = e as Error
if (err.message.includes("Unexpected file mode string: 120000")) {
console.log(`
⛔️ ${chalk.red.bold("ERROR")}

Expand All @@ -358,7 +375,7 @@ export function makePatch({
outPath,
gzipSync(
JSON.stringify({
error: { message: e.message, stack: e.stack },
error: { message: err.message, stack: err.stack },
patch: diffResult.stdout.toString(),
}),
),
Expand Down Expand Up @@ -544,7 +561,12 @@ export function makePatch({
}
}
} catch (e) {
console.log(e)
const err = e as Error & {
stdout?: Buffer
stderr?: Buffer
}
// try to log more useful error message
console.log(err.stderr?.toString() || err.stdout?.toString() || e)
throw e
} finally {
tmpRepo.removeCallback()
Expand Down