'use strict'; const { test } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); const { execFileSync } = require('node:child_process'); test('release commits manifest and atomic lock metadata together without dependency resolution or push', t => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ped-release-')); t.after(() => fs.rmSync(root, { recursive: true, force: true })); const run = (command, args) => execFileSync(command, args, { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env, npm_config_offline: 'true' }, }).trim(); const write = (file, data) => fs.writeFileSync(path.join(root, file), JSON.stringify(data, null, 2) + '\n'); const read = file => JSON.parse(fs.readFileSync(path.join(root, file), 'utf8')); fs.mkdirSync(path.join(root, 'scripts')); fs.mkdirSync(path.join(root, 'mobile/android/app'), { recursive: true }); fs.copyFileSync(path.join(__dirname, '../scripts/release.sh'), path.join(root, 'scripts/release.sh')); const manifest = { nested: { version: 'leave-alone' }, version: '1.0.0', dependencies: { example: '^1.0.0' } }; const lock = { name: 'test', version: '0.9.0', lockfileVersion: 3, packages: { '': { name: 'test', version: '0.9.0', dependencies: manifest.dependencies }, 'node_modules/example': { version: '1.2.3', resolved: 'https://example.invalid/never-fetch.tgz', integrity: 'unchanged' }, } }; write('package.json', manifest); write('mobile/package.json', manifest); write('package-lock.json', lock); fs.writeFileSync(path.join(root, 'mobile/android/app/build.gradle'), 'versionCode 100000\nversionName "1.0.0"\n'); run('git', ['init']); run('git', ['config', 'user.name', 'Release Test']); run('git', ['config', 'user.email', 'release@example.invalid']); run('git', ['config', 'commit.gpgsign', 'false']); run('git', ['config', 'tag.gpgsign', 'false']); run('git', ['add', '.']); run('git', ['commit', '-m', 'fixture']); // No remote exists: a successful run cannot have pushed anything. run('bash', ['scripts/release.sh', '2.3.4']); const expected = { ...manifest, version: '2.3.4' }; assert.deepEqual(read('package.json'), expected); assert.deepEqual(read('mobile/package.json'), expected); lock.version = lock.packages[''].version = '2.3.4'; assert.deepEqual(read('package-lock.json'), lock); assert.match(fs.readFileSync(path.join(root, 'mobile/android/app/build.gradle'), 'utf8'), /versionCode 203004\nversionName "2.3.4"/); assert.equal(run('git', ['status', '--porcelain']), ''); assert.deepEqual(run('git', ['show', '--format=', '--name-only', 'HEAD']).split('\n').sort(), ['mobile/android/app/build.gradle', 'mobile/package.json', 'package-lock.json', 'package.json']); assert.equal(run('git', ['rev-parse', 'v2.3.4^{commit}']), run('git', ['rev-parse', 'HEAD'])); assert.equal(fs.existsSync(path.join(root, 'node_modules')), false); // An invalid lock fails before either manifest changes or a release commit is made. fs.writeFileSync(path.join(root, 'package-lock.json'), '{invalid'); run('git', ['add', 'package-lock.json']); run('git', ['commit', '-m', 'invalid lock fixture']); const head = run('git', ['rev-parse', 'HEAD']); assert.throws(() => run('bash', ['scripts/release.sh', '2.3.5'])); assert.deepEqual(read('package.json'), expected); assert.deepEqual(read('mobile/package.json'), expected); assert.equal(run('git', ['status', '--porcelain']), ''); assert.equal(run('git', ['rev-parse', 'HEAD']), head); });