Skip to content

Commit 3a28314

Browse files
brooke-hamiltonchrmarti
authored andcommitted
fix newline issue
Signed-off-by: Brooke Hamilton <[email protected]>
1 parent da642b4 commit 3a28314

8 files changed

Lines changed: 57 additions & 11 deletions

File tree

.vscode/settings.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@
33
"search.exclude": {
44
"dist": true
55
},
6-
"typescript.tsc.autoDetect": "off",
6+
"js/ts.tsc.autoDetect": "off",
77
"eslint.options": {
88
"rulePaths": [
99
"./build/eslint"
1010
]
1111
},
12-
"mochaExplorer.files": "test/**/*.test.ts",
12+
"mochaExplorer.files": "src/test/**/*.test.ts",
1313
"mochaExplorer.require": "ts-node/register",
1414
"mochaExplorer.env": {
1515
"TS_NODE_PROJECT": "src/test/tsconfig.json"
1616
},
1717
"files.associations": {
1818
"devcontainer-features.json": "jsonc"
1919
},
20-
"typescript.tsdk": "node_modules/typescript/lib",
20+
"js/ts.tsdk.path": "node_modules/typescript/lib",
2121
"git.branchProtection": [
2222
"main",
2323
"release/*"

src/spec-configuration/lockfile.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,20 @@ export async function writeLockfile(params: ContainerFeatureInternalParams, conf
5353
return;
5454
}
5555

56-
const newLockfileContentString = JSON.stringify(lockfile, null, 2);
56+
const newLockfileContentString = JSON.stringify(lockfile, null, 2) + '\n';
5757
const newLockfileContent = Buffer.from(newLockfileContentString);
5858
if (params.experimentalFrozenLockfile && !oldLockfileContent) {
5959
throw new Error('Lockfile does not exist.');
6060
}
61-
if (!oldLockfileContent || !newLockfileContent.equals(oldLockfileContent)) {
61+
let oldLockfileNormalized: string | undefined;
62+
if (oldLockfileContent) {
63+
try {
64+
oldLockfileNormalized = JSON.stringify(JSON.parse(oldLockfileContent.toString()), null, 2) + '\n';
65+
} catch {
66+
// Empty or invalid JSON; treat as needing rewrite.
67+
}
68+
}
69+
if (!oldLockfileNormalized || oldLockfileNormalized !== newLockfileContentString) {
6270
if (params.experimentalFrozenLockfile) {
6371
throw new Error('Lockfile does not match.');
6472
}

src/test/container-features/configs/lockfile-dependson/expected.devcontainer-lock.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@
2727
]
2828
}
2929
}
30-
}
30+
}

src/test/container-features/configs/lockfile-frozen/.devcontainer-lock.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@
1111
"integrity": "sha256:c9cc1ac636b9ef595512b5ca7ecb3a35b7d3499cb6f86372edec76ae0cd71d43"
1212
}
1313
}
14-
}
14+
}

src/test/container-features/configs/lockfile-outdated/expected.devcontainer-lock.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@
1111
"integrity": "sha256:c9cc1ac636b9ef595512b5ca7ecb3a35b7d3499cb6f86372edec76ae0cd71d43"
1212
}
1313
}
14-
}
14+
}

src/test/container-features/configs/lockfile-upgrade-command/upgraded.devcontainer-lock.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@
2121
"integrity": "sha256:9024deeca80347dea7603a3bb5b4951988f0bf5894ba036a6ee3f29c025692c6"
2222
}
2323
}
24-
}
24+
}

src/test/container-features/configs/lockfile/expected.devcontainer-lock.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
"integrity": "sha256:41607bd6aba3975adcd0641cc479e67b04abd21763ba8a41ea053bcc04a6a818"
1717
}
1818
}
19-
}
19+
}

src/test/container-features/lockfile.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as assert from 'assert';
77
import * as path from 'path';
88
import * as semver from 'semver';
99
import { shellExec } from '../testUtils';
10-
import { cpLocal, readLocalFile, rmLocal } from '../../spec-utils/pfs';
10+
import { cpLocal, readLocalFile, rmLocal, writeLocalFile } from '../../spec-utils/pfs';
1111

1212
const pkg = require('../../../package.json');
1313

@@ -279,6 +279,44 @@ describe('Lockfile', function () {
279279
}
280280
});
281281

282+
it('lockfile ends with trailing newline', async () => {
283+
const workspaceFolder = path.join(__dirname, 'configs/lockfile');
284+
285+
const lockfilePath = path.join(workspaceFolder, '.devcontainer-lock.json');
286+
await rmLocal(lockfilePath, { force: true });
287+
288+
const res = await shellExec(`${cli} build --workspace-folder ${workspaceFolder} --experimental-lockfile`);
289+
const response = JSON.parse(res.stdout);
290+
assert.equal(response.outcome, 'success');
291+
const actual = (await readLocalFile(lockfilePath)).toString();
292+
assert.ok(actual.endsWith('\n'), 'Lockfile should end with a trailing newline');
293+
});
294+
295+
it('frozen lockfile matches despite formatting differences', async () => {
296+
const workspaceFolder = path.join(__dirname, 'configs/lockfile-frozen');
297+
const lockfilePath = path.join(workspaceFolder, '.devcontainer-lock.json');
298+
299+
// Read the existing lockfile, strip trailing newline to create a byte-different but semantically identical file
300+
const original = (await readLocalFile(lockfilePath)).toString();
301+
const stripped = original.replace(/\n$/, '');
302+
assert.notEqual(original, stripped, 'Test setup: should have removed trailing newline');
303+
assert.deepEqual(JSON.parse(original), JSON.parse(stripped), 'Test setup: JSON content should be identical');
304+
305+
try {
306+
await writeLocalFile(lockfilePath, Buffer.from(stripped));
307+
308+
// Frozen lockfile should succeed because JSON content is the same
309+
const res = await shellExec(`${cli} build --workspace-folder ${workspaceFolder} --experimental-lockfile --experimental-frozen-lockfile`);
310+
const response = JSON.parse(res.stdout);
311+
assert.equal(response.outcome, 'success', 'Frozen lockfile should not fail when only formatting differs');
312+
const actual = (await readLocalFile(lockfilePath)).toString();
313+
assert.strictEqual(actual, stripped, 'Frozen lockfile should remain unchanged when only formatting differs');
314+
} finally {
315+
// Restore original lockfile
316+
await writeLocalFile(lockfilePath, Buffer.from(original));
317+
}
318+
});
319+
282320
it('upgrade command should work with default workspace folder', async () => {
283321
const workspaceFolder = path.join(__dirname, 'configs/lockfile-upgrade-command');
284322
const absoluteTmpPath = path.resolve(__dirname, 'tmp');

0 commit comments

Comments
 (0)