Skip to content
Merged
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
50 changes: 48 additions & 2 deletions tools/release/public-consumer-smoke.mts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import {
} from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { EXTENSION_PORTABLE_TARGET } from '../../src/wasix/runtime/tools/wasix-cargo-artifact-contract.mts';
import { DEFAULT_PUBLICATION_LOCK, loadPublicationLock } from './publication-lock.mts';
import { registryRetryDelaySeconds, registryStatusRetryable } from './registry-http-retry.mts';
import { validateRegistryReceiptEvidence } from './registry-integrity.mts';
import { DESKTOP_TARGETS } from './release-artifact-targets.mts';
import { compareText, loadProducts, ROOT } from './release-graph.mts';
import { validateGithubAttestationReceipt } from './verify_github_release_attestations.mts';

Expand Down Expand Up @@ -915,6 +917,28 @@ export function validateNpmResolution(packageLock, carriers, requiredEntryIds, n
return { resolved, installedCarrierIds };
}

function npmEntryPlatform(carrier) {
if (
carrier.target == null ||
carrier.target === 'portable' ||
carrier.target === EXTENSION_PORTABLE_TARGET
)
return null;
const target =
{ 'darwin-arm64': 'macos-arm64', 'win32-x64-msvc': 'windows-x64-msvc' }[carrier.target] ??
carrier.target;
const profile = DESKTOP_TARGETS[target];
if (!profile?.npmOs || !profile.npmCpu)
throw error(
`${carrier.id} has unsupported npm consumer target ${JSON.stringify(carrier.target)}`,
);
return {
os: profile.npmOs,
cpu: profile.npmCpu,
...(profile.npmLibc ? { libc: profile.npmLibc } : {}),
};
}

function npmSurface({ lock, surface, root }, prepare) {
const directory = path.join(root, 'npm');
const home = path.join(root, 'npm-home');
Expand All @@ -934,21 +958,35 @@ function npmSurface({ lock, surface, root }, prepare) {
NPM_CONFIG_USERCONFIG: userConfig,
});
const entries = [];
const entryPlatforms = [];
const rows = [];
const installed = new Set();
for (const [index, entryCarrierId] of surface.entryCarrierIds.entries()) {
const carrier = byId.get(entryCarrierId);
const platform = npmEntryPlatform(carrier);
entryPlatforms.push({ entryCarrierId, platform });
const consumer = path.join(directory, `entry-${String(index).padStart(3, '0')}`);
if (prepare) {
mkdirSync(consumer, { recursive: true });
// npm honors platform overrides for optional dependencies. The exact
// entry must still be installed below; --ignore-scripts avoids execution.
if (platform !== null)
writeFileSync(
path.join(consumer, '.npmrc'),
Object.entries(platform)
.map(([key, value]) => `${key}=${value}\n`)
.join(''),
);
writeFileSync(
path.join(consumer, 'package.json'),
`${JSON.stringify(
{
name: `oliphaunt-public-consumer-smoke-${String(index).padStart(3, '0')}`,
version: '0.0.0',
private: true,
dependencies: { [carrier.name]: carrier.version },
[platform === null ? 'dependencies' : 'optionalDependencies']: {
[carrier.name]: carrier.version,
},
},
null,
2,
Expand All @@ -965,9 +1003,10 @@ function npmSurface({ lock, surface, root }, prepare) {
if (prepare) return env;
return {
surface: 'npm',
mode: 'anonymous-public-independent-entry-host-install-and-lock-resolution',
mode: 'anonymous-public-independent-entry-platform-install-and-lock-resolution',
registry: 'https://registry.npmjs.org',
host: `${process.platform}-${process.arch}`,
entryPlatforms,
...resolvedSurfaceCoverage(surface, entries, rows),
installedCarrierIds: [...installed].sort(compareText),
receiptCoveredNotHostInstalledCarrierIds: surface.carrierIds
Expand Down Expand Up @@ -1268,6 +1307,13 @@ export function validatePublicConsumerEvidence(evidence, lock, plan) {
}
}
if (surface.ecosystem === 'npm') {
const byId = new Map(lock.carriers.map((carrier) => [carrier.id, carrier]));
const platforms = surface.entryCarrierIds.map((entryCarrierId) => ({
entryCarrierId,
platform: npmEntryPlatform(byId.get(entryCarrierId)),
}));
if (stableJson(observed.entryPlatforms) !== stableJson(platforms))
throw error('npm entry platforms differ from the frozen carrier targets');
const installed = sortedUniqueStrings(
observed?.installedCarrierIds ?? [],
'npm installedCarrierIds',
Expand Down
87 changes: 78 additions & 9 deletions tools/release/public-consumer-smoke.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ function graph(products) {
};
}

const npmPlatformFixtures = [
{ target: null, platform: null },
{ target: 'portable', platform: null },
{ target: 'linux-arm64-gnu', platform: { os: 'linux', cpu: 'arm64', libc: 'glibc' } },
{ target: 'linux-x64-gnu', platform: { os: 'linux', cpu: 'x64', libc: 'glibc' } },
{ target: 'macos-arm64', platform: { os: 'darwin', cpu: 'arm64' } },
{ target: 'darwin-arm64', platform: { os: 'darwin', cpu: 'arm64' } },
{ target: 'windows-x64-msvc', platform: { os: 'win32', cpu: 'x64' } },
{ target: 'win32-x64-msvc', platform: { os: 'win32', cpu: 'x64' } },
{ target: 'wasix-portable', platform: null },
];

const [fixtureMode, fixtureRoot, scenario] = process.argv.slice(2);
if (fixtureMode === 'prepare-cargo') {
const environment = publicCargoEnvironment(fixtureRoot, {
Expand Down Expand Up @@ -94,7 +106,17 @@ if (fixtureMode === 'prepare-cargo') {
}
if (fixtureMode === 'prepare-npm') {
const products = [product('sdk', ['npm'])];
const frozen = lock(products, [carrier('npm:@example/sdk', 'sdk', 0)]);
const carriers =
scenario === 'platforms'
? npmPlatformFixtures.map(({ target }, index) => ({
...carrier(`npm:@example/platform-${index}`, 'sdk', index),
target,
}))
: scenario === 'missing-entry'
? [{ ...carrier('npm:@example/platform-5', 'sdk', 0), target: 'darwin-arm64' }]
: [carrier('npm:@example/sdk', 'sdk', 0)];
if (scenario === 'unknown-platform') carriers[0].target = 'unknown-platform';
const frozen = lock(products, carriers);
writeFileSync(
path.join(fixtureRoot, 'context.json'),
JSON.stringify({
Expand All @@ -107,9 +129,23 @@ if (fixtureMode === 'prepare-npm') {
}
if (fixtureMode === 'install-npm') {
const manifest = JSON.parse(readFileSync('package.json', 'utf8'));
const [[name, version]] = Object.entries(manifest.dependencies);
mkdirSync(`node_modules/${name}`, { recursive: true });
writeFileSync(`node_modules/${name}/package.json`, JSON.stringify({ name, version }));
const [[name, version]] = Object.entries(manifest.dependencies ?? manifest.optionalDependencies);
if (name.startsWith('@example/platform-')) {
const { platform } = npmPlatformFixtures[Number(name.split('-').at(-1))];
assert.equal(manifest.optionalDependencies !== undefined, platform !== null);
if (platform === null) assert.equal(existsSync('.npmrc'), false);
else
assert.equal(
readFileSync('.npmrc', 'utf8'),
Object.entries(platform)
.map(([key, value]) => `${key}=${value}\n`)
.join(''),
);
}
if (!process.env.OMIT_PUBLIC_PROBE) {
mkdirSync(`node_modules/${name}`, { recursive: true });
writeFileSync(`node_modules/${name}/package.json`, JSON.stringify({ name, version }));
}
writeFileSync(
'package-lock.json',
JSON.stringify({
Expand All @@ -126,12 +162,43 @@ if (fixtureMode === 'install-npm') {
process.exit(0);
}
if (fixtureMode === 'assert-npm') {
if (scenario === 'success') {
if (scenario === 'success' || scenario === 'platforms') {
const result = JSON.parse(readFileSync(path.join(fixtureRoot, 'npm.json'), 'utf8'));
assert.deepEqual(result.installedCarrierIds, ['npm:@example/sdk']);
const context = JSON.parse(readFileSync(path.join(fixtureRoot, 'context.json'), 'utf8'));
const ids = context.lock.carriers.map(({ id }) => id);
assert.equal(
result.mode,
'anonymous-public-independent-entry-platform-install-and-lock-resolution',
);
assert.deepEqual(result.installedCarrierIds, ids);
assert.deepEqual(
result.resolved.map(({ id }) => id),
['npm:@example/sdk'],
ids,
);
assert.deepEqual(
result.entryPlatforms,
ids.map((entryCarrierId, index) => ({
entryCarrierId,
platform: scenario === 'platforms' ? npmPlatformFixtures[index].platform : null,
})),
);
const evidence = publicConsumerEvidence({
...context,
registryReceiptSha256: 'e'.repeat(64),
githubReceiptDigest: 'f'.repeat(64),
surfaces: [
result,
{ surface: 'github', productTags: context.plan.github.productTags, swift: null },
],
});
validatePublicConsumerEvidence(evidence, context.lock, context.plan);
evidence.surfaces.find(({ surface }) => surface === 'npm').entryPlatforms[0].platform = {
os: 'linux',
cpu: 'x64',
};
assert.throws(
() => validatePublicConsumerEvidence(evidence, context.lock, context.plan),
/npm entry platforms differ from the frozen carrier targets/u,
);
} else assert.equal(existsSync(path.join(fixtureRoot, 'npm.json')), false);
process.exit(0);
Expand Down Expand Up @@ -667,10 +734,11 @@ test('builds canonical lock/receipt-bound evidence and writes it immutably', ()
const surfaces = [
{
surface: 'npm',
mode: 'anonymous-public-independent-entry-host-install-and-lock-resolution',
mode: 'anonymous-public-independent-entry-platform-install-and-lock-resolution',
carrierIds: ['npm:@example/alpha'],
dependencyScopes: ['optional', 'peer', 'runtime'],
entryCarrierIds: ['npm:@example/alpha'],
entryPlatforms: [{ entryCarrierId: 'npm:@example/alpha', platform: null }],
plannedEntryClosures: [
{ entryCarrierId: 'npm:@example/alpha', carrierIds: ['npm:@example/alpha'] },
],
Expand Down Expand Up @@ -735,10 +803,11 @@ test('cannot silently relabel a frozen entry dependency as receipt-only', () =>
surfaces: [
{
surface: 'npm',
mode: 'anonymous-public-independent-entry-host-install-and-lock-resolution',
mode: 'anonymous-public-independent-entry-platform-install-and-lock-resolution',
carrierIds: ['npm:@example/alpha', 'npm:@example/leaf'],
dependencyScopes: ['optional', 'peer', 'runtime'],
entryCarrierIds: ['npm:@example/alpha'],
entryPlatforms: [{ entryCarrierId: 'npm:@example/alpha', platform: null }],
plannedEntryClosures: [
{
entryCarrierId: 'npm:@example/alpha',
Expand Down
10 changes: 7 additions & 3 deletions tools/release/public-consumer-smoke.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,25 @@ SH
chmod +x "$scratch/bin/npm"
export PATH="$scratch/bin:$PATH" SENSITIVE_TOKEN=must-not-survive CARGO_REGISTRY_TOKEN=must-not-survive
export PUBLIC_PROBE_COUNTER="$scratch/attempts" PUBLIC_PROBE_FIXTURE="$source_root/tools/release/public-consumer-smoke.test.mts"
for mode in success fail timeout expired; do
for mode in success platforms unknown-platform missing-entry fail timeout expired; do
mkdir "$scratch/$mode"
bun tools/release/public-consumer-smoke.test.mts prepare-npm "$scratch/$mode" "$mode"
unset FAIL_PUBLIC_PROBE HANG_PUBLIC_PROBE PUBLIC_PROBE_CHILD
unset FAIL_PUBLIC_PROBE HANG_PUBLIC_PROBE OMIT_PUBLIC_PROBE PUBLIC_PROBE_CHILD
expected=0
if [[ "$mode" == expired ]]; then expected=1; fi
if [[ "$mode" == unknown-platform ]]; then expected=1; fi
if [[ "$mode" == missing-entry ]]; then export OMIT_PUBLIC_PROBE=1; expected=1; fi
if [[ "$mode" == fail ]]; then export FAIL_PUBLIC_PROBE=1; expected=7; fi
if [[ "$mode" == timeout ]]; then export HANG_PUBLIC_PROBE=1 PUBLIC_PROBE_CHILD="$scratch/child-pid"; expected=124; fi
status=0
bash tools/release/public-consumer-smoke.sh --surface "$scratch/$mode" npm > "$scratch/$mode/output" 2>&1 || status=$?
if [[ "$status" != "$expected" ]]; then cat "$scratch/$mode/output" >&2; exit 1; fi
if [[ "$mode" == expired ]]; then grep -q "shared public-consumer deadline reached" "$scratch/$mode/output"; fi
if [[ "$mode" == unknown-platform ]]; then grep -q "unsupported npm consumer target" "$scratch/$mode/output"; fi
if [[ "$mode" == missing-entry ]]; then grep -q "entry package was not installed from the public registry" "$scratch/$mode/output"; fi
bun tools/release/public-consumer-smoke.test.mts assert-npm "$scratch/$mode" "$mode"
done
[[ "$(wc -l < "$scratch/attempts")" -eq 3 ]]
[[ "$(wc -l < "$scratch/attempts")" -eq 13 ]]
pid="$(cat "$scratch/child-pid")"
status=0
state="$(ps -o stat= -p "$pid")" || status=$?
Expand Down