Skip to content
Draft
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
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@
"scripts": {
"commit": "commit",
"test": "jest",
"test:package-install": "node ./scripts/test-package-install.js",
"typescript": "tsc --noEmit",
"lint": "eslint \"**/*.{js,ts,tsx}\"",
"reset": "git clean -xdfe .vscode && yarn && yarn prepare",
"markbuild": "tsx ./scripts/example-dot-env.ts",
"prepare": "bob build && yarn markbuild",
"postinstall": "husky",
"prepack": "pinst --disable",
"postpack": "pinst --enable",
"example": "yarn --cwd example",
"pods": "cd example && pod-install --quiet",
"bootstrap": "yarn && yarn pods && yarn markbuild",
Expand Down Expand Up @@ -93,6 +96,7 @@
"@types/react-test-renderer": "^19.1.0",
"chalk": "^5.3.0",
"commitlint": "^19.8.1",
"cross-spawn": "^7.0.6",
"detox": "^20.45.1",
"eslint": "^9.35.0",
"eslint-config-prettier": "^10.1.8",
Expand All @@ -103,6 +107,7 @@
"husky": "^9.1.7",
"inquirer": "^9.2.7",
"jest": "^29.7.0",
"pinst": "^3.0.0",
"pod-install": "^1.0.7",
"prettier": "^3.6.2",
"react": "19.1.1",
Expand Down
202 changes: 202 additions & 0 deletions scripts/test-package-install.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
const assert = require('node:assert/strict');
const crossSpawn = require('cross-spawn');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');

const packageRoot = path.resolve(__dirname, '..');
const sourceManifestPath = path.join(packageRoot, 'package.json');
const sourceManifestText = fs.readFileSync(sourceManifestPath, 'utf8');
const sourceManifest = JSON.parse(sourceManifestText);
const tempRoot = fs.mkdtempSync(
path.join(os.tmpdir(), 'amazon-ivs-package-install-')
);

function run(command, args, options) {
return crossSpawn.sync(command, args, {
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024,
...options,
});
}

function commandOutput(result) {
return [
`status: ${result.status}`,
`signal: ${result.signal}`,
`stdout:\n${result.stdout || ''}`,
`stderr:\n${result.stderr || ''}`,
].join('\n');
}

function assertCommandStarted(result, description) {
assert.equal(
result.error == null,
true,
`${description} could not start: ${result.error}`
);
assert.equal(
result.signal,
null,
`${description} was terminated by ${result.signal}\n${commandOutput(
result
)}`
);
}

try {
assert.equal(
sourceManifest.scripts.postinstall,
'husky',
'the source manifest must keep installing contributor Git hooks'
);
assert.equal(
sourceManifest.scripts._postinstall,
undefined,
'the source manifest started with a disabled postinstall script'
);

const tarballPath = path.join(tempRoot, 'amazon-ivs-react-native-player.tgz');
const packResult = run('corepack', ['yarn', 'pack', '--out', tarballPath], {
cwd: packageRoot,
});

assertCommandStarted(packResult, 'yarn pack');
assert.equal(
packResult.status,
0,
`yarn pack failed\n${commandOutput(packResult)}`
);
assert.ok(
fs.statSync(tarballPath).size > 0,
'yarn pack created an empty archive'
);

const restoredManifest = JSON.parse(
fs.readFileSync(sourceManifestPath, 'utf8')
);
assert.equal(
restoredManifest.scripts.postinstall,
sourceManifest.scripts.postinstall,
'yarn pack did not restore the source postinstall script'
);
assert.equal(
restoredManifest.scripts._postinstall,
undefined,
'yarn pack left a disabled postinstall script in the source manifest'
);

const consumerRoot = path.join(tempRoot, 'consumer');
const cacheRoot = path.join(tempRoot, 'npm-cache');
const guardBin = path.join(tempRoot, 'guard-bin');
const guardMarker = path.join(tempRoot, 'husky-called');
const userConfig = path.join(tempRoot, 'npmrc');

fs.mkdirSync(consumerRoot);
fs.mkdirSync(cacheRoot);
fs.mkdirSync(guardBin);
fs.writeFileSync(
path.join(consumerRoot, 'package.json'),
`${JSON.stringify(
{
name: 'package-install-smoke-test',
version: '1.0.0',
private: true,
},
null,
2
)}\n`
);
fs.writeFileSync(userConfig, '');

const posixGuard = path.join(guardBin, 'husky');
fs.writeFileSync(
posixGuard,
'#!/bin/sh\n: > "$HUSKY_GUARD_MARKER"\nexit 86\n'
);
fs.chmodSync(posixGuard, 0o755);
fs.writeFileSync(
path.join(guardBin, 'husky.cmd'),
'@echo off\r\ntype nul > "%HUSKY_GUARD_MARKER%"\r\nexit /b 86\r\n'
);

const pathKey =
Object.keys(process.env).find((key) => key.toLowerCase() === 'path') ||
'PATH';
const installEnvironment = {
...process.env,
HUSKY_GUARD_MARKER: guardMarker,
npm_config_ignore_scripts: 'false',
npm_config_update_notifier: 'false',
npm_config_userconfig: userConfig,
};
installEnvironment[pathKey] = `${guardBin}${path.delimiter}${
process.env[pathKey] || ''
}`;

const installResult = run(
'npm',
[
'install',
'--offline',
'--legacy-peer-deps',
'--omit=dev',
'--no-save',
'--no-package-lock',
'--no-audit',
'--no-fund',
'--foreground-scripts',
'--ignore-scripts=false',
'--cache',
cacheRoot,
tarballPath,
],
{
cwd: consumerRoot,
env: installEnvironment,
}
);

assertCommandStarted(installResult, 'consumer npm install');
assert.equal(
fs.existsSync(guardMarker),
false,
`the packed dependency invoked Husky during consumer installation\n${commandOutput(
installResult
)}`
);
assert.equal(
installResult.status,
0,
`consumer npm install failed\n${commandOutput(installResult)}`
);

const installedManifest = JSON.parse(
fs.readFileSync(
path.join(
consumerRoot,
'node_modules',
sourceManifest.name,
'package.json'
),
'utf8'
)
);
assert.equal(installedManifest.name, sourceManifest.name);
assert.equal(installedManifest.version, sourceManifest.version);
assert.equal(
installedManifest.scripts.postinstall,
undefined,
'the packed dependency retained an active postinstall script'
);
} finally {
if (fs.readFileSync(sourceManifestPath, 'utf8') !== sourceManifestText) {
fs.writeFileSync(sourceManifestPath, sourceManifestText);
}
fs.rmSync(tempRoot, {
recursive: true,
force: true,
maxRetries: 3,
retryDelay: 100,
});
}
11 changes: 11 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3921,6 +3921,7 @@ __metadata:
"@types/react-test-renderer": ^19.1.0
chalk: ^5.3.0
commitlint: ^19.8.1
cross-spawn: ^7.0.6
detox: ^20.45.1
eslint: ^9.35.0
eslint-config-prettier: ^10.1.8
Expand All @@ -3931,6 +3932,7 @@ __metadata:
husky: ^9.1.7
inquirer: ^9.2.7
jest: ^29.7.0
pinst: ^3.0.0
pod-install: ^1.0.7
prettier: ^3.6.2
react: 19.1.1
Expand Down Expand Up @@ -9717,6 +9719,15 @@ __metadata:
languageName: node
linkType: hard

"pinst@npm:^3.0.0":
version: 3.0.0
resolution: "pinst@npm:3.0.0"
bin:
pinst: bin.js
checksum: 4ae48a6a60f79c37071233af51b4d91bfc85cfa3c12b66ccda60cdb642b4d14a4ab0cb3587afc55b1f6192cea1772a5e4822026a0d0d3528296edef00cc2d61f
languageName: node
linkType: hard

"pirates@npm:^4.0.4":
version: 4.0.7
resolution: "pirates@npm:4.0.7"
Expand Down