Skip to content

Commit 487d943

Browse files
Build pre-release VSIX in CI (#1450)
1 parent b871ec5 commit 487d943

File tree

8 files changed

+187
-34
lines changed

8 files changed

+187
-34
lines changed

.github/workflows/nightly.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ jobs:
2424
npm ci
2525
npm run compile
2626
npm run package
27+
npm run preview-package
2728
- name: Archive production artifacts
2829
uses: actions/upload-artifact@v4
2930
if: always()

.vscode/launch.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@
4545
"request": "launch",
4646
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/tsx",
4747
"runtimeArgs": ["${workspaceFolder}/scripts/update_swift_docc_render.ts"]
48+
},
49+
{
50+
"name": "Preview Package",
51+
"type": "node",
52+
"request": "launch",
53+
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/tsx",
54+
"runtimeArgs": ["${workspaceFolder}/scripts/preview_package.ts"]
4855
}
4956
]
5057
}

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "swift-vscode",
33
"displayName": "Swift",
44
"description": "Swift Language Support for Visual Studio Code.",
5-
"version": "2.1.0",
5+
"version": "2.0.2",
66
"publisher": "swiftlang",
77
"icon": "icon.png",
88
"repository": {
@@ -1630,14 +1630,15 @@
16301630
"postinstall": "npm run update-swift-docc-render",
16311631
"pretest": "npm run compile-tests",
16321632
"soundness": "scripts/soundness.sh",
1633+
"check-package-json": "tsx ./scripts/check_package_json.ts",
16331634
"test": "vscode-test",
16341635
"integration-test": "npm test -- --label integrationTests",
16351636
"unit-test": "npm test -- --label unitTests",
16361637
"coverage": "npm test -- --coverage",
16371638
"compile-tests": "del-cli ./assets/test/**/.build && npm run compile",
16381639
"package": "vsce package",
16391640
"dev-package": "vsce package --no-update-package-json 2.1.0-dev",
1640-
"preview-package": "vsce package --pre-release",
1641+
"preview-package": "tsx ./scripts/preview_package.ts",
16411642
"tag": "./scripts/tag_release.sh $npm_package_version",
16421643
"contributors": "./scripts/generate_contributors_list.sh"
16431644
},

scripts/check_package_json.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2025 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
/* eslint-disable no-console */
15+
16+
import { getExtensionVersion, main } from "./lib/utilities";
17+
18+
main(async () => {
19+
const version = await getExtensionVersion();
20+
if (version.minor % 2 !== 0) {
21+
throw new Error(
22+
`Invalid version number in package.json. ${version.toString()} does not have an even numbered minor version.`
23+
);
24+
}
25+
});

scripts/lib/utilities.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2025 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
/* eslint-disable no-console */
15+
16+
import * as child_process from "child_process";
17+
import { readFile } from "fs/promises";
18+
import * as path from "path";
19+
import * as semver from "semver";
20+
21+
/**
22+
* Executes the provided main function for the script while logging any errors.
23+
*
24+
* If an error is caught then the process will exit with code 1.
25+
*
26+
* @param mainFn The main function of the script that will be run.
27+
*/
28+
export async function main(mainFn: () => Promise<void>): Promise<void> {
29+
try {
30+
await mainFn();
31+
} catch (error) {
32+
console.error(error);
33+
process.exit(1);
34+
}
35+
}
36+
37+
/**
38+
* Returns the root directory of the repository.
39+
*/
40+
export function getRootDirectory(): string {
41+
return path.join(__dirname, "..", "..");
42+
}
43+
44+
/**
45+
* Retrieves the version number from the package.json.
46+
*/
47+
export async function getExtensionVersion(): Promise<semver.SemVer> {
48+
const packageJSON = JSON.parse(
49+
await readFile(path.join(getRootDirectory(), "package.json"), "utf-8")
50+
);
51+
if (typeof packageJSON.version !== "string") {
52+
throw new Error("Version number in package.json is not a string");
53+
}
54+
const version = semver.parse(packageJSON.version);
55+
if (version === null) {
56+
throw new Error("Unable to parse version number in package.json");
57+
}
58+
return version;
59+
}
60+
61+
/**
62+
* Executes the given command, inheriting the current process' stdio.
63+
*
64+
* @param command The command to execute.
65+
* @param args The arguments to provide to the command.
66+
* @param options The options for executing the command.
67+
*/
68+
export async function exec(
69+
command: string,
70+
args: string[],
71+
options: child_process.SpawnOptionsWithoutStdio = {}
72+
): Promise<void> {
73+
let logMessage = "> " + command;
74+
if (args.length > 0) {
75+
logMessage += " " + args.join(" ");
76+
}
77+
console.log(logMessage + "\n");
78+
return new Promise<void>((resolve, reject) => {
79+
const childProcess = child_process.spawn(command, args, { stdio: "inherit", ...options });
80+
childProcess.once("error", reject);
81+
childProcess.once("close", (code, signal) => {
82+
if (signal !== null) {
83+
reject(new Error(`Process exited due to signal '${signal}'`));
84+
} else if (code !== 0) {
85+
reject(new Error(`Process exited with code ${code}`));
86+
} else {
87+
resolve();
88+
}
89+
console.log("");
90+
});
91+
});
92+
}

scripts/preview_package.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2025 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
/* eslint-disable no-console */
15+
16+
import { exec, getExtensionVersion, getRootDirectory, main } from "./lib/utilities";
17+
18+
/**
19+
* Formats the given date as a string in the form "YYYYMMddhhmm".
20+
*
21+
* @param date The date to format as a string.
22+
* @returns The formatted date.
23+
*/
24+
function formatDate(date: Date): string {
25+
const year = date.getUTCFullYear().toString().padStart(4, "0");
26+
const month = (date.getUTCMonth() + 1).toString().padStart(2, "0");
27+
const day = date.getUTCDate().toString().padStart(2, "0");
28+
const hour = date.getUTCHours().toString().padStart(2, "0");
29+
const minutes = date.getUTCMinutes().toString().padStart(2, "0");
30+
return year + month + day + hour + minutes;
31+
}
32+
33+
main(async () => {
34+
const rootDirectory = getRootDirectory();
35+
const version = await getExtensionVersion();
36+
// Increment the minor version and set the patch version to today's date
37+
const minor = version.minor + 1;
38+
const patch = formatDate(new Date());
39+
const previewVersion = `${version.major}.${minor}.${patch}`;
40+
// Make sure that the new minor version is odd
41+
if (minor % 2 !== 1) {
42+
throw new Error(
43+
`The minor version for the pre-release extension is even (${previewVersion}).` +
44+
" The version in the package.json has probably been incorrectly set to an odd minor version."
45+
);
46+
}
47+
// Use VSCE to package the extension
48+
await exec(
49+
"npx",
50+
["vsce", "package", "--pre-release", "--no-update-package-json", previewVersion],
51+
{ cwd: rootDirectory }
52+
);
53+
});

scripts/soundness.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ function replace_acceptable_years() {
3636
sed -e 's/20[12][0123456789]-20[12][0123456789]/YEARS/' -e 's/20[12][0123456789]/YEARS/'
3737
}
3838

39+
printf "=> Checking package.json..."
40+
npm run check-package-json
41+
3942
printf "=> Checking license headers... "
4043
tmp=$(mktemp /tmp/.vscode-swift-soundness_XXXXXX)
4144

scripts/update_swift_docc_render.ts

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
/* eslint-disable no-console */
1515

1616
import simpleGit, { ResetMode } from "simple-git";
17-
import { spawn } from "child_process";
1817
import { stat, mkdtemp, mkdir, rm, readdir } from "fs/promises";
1918
import * as path from "path";
2019
import { tmpdir } from "os";
2120
import * as semver from "semver";
21+
import { exec, getRootDirectory, main } from "./lib/utilities";
2222

2323
function checkNodeVersion() {
2424
const nodeVersion = semver.parse(process.versions.node);
@@ -57,34 +57,8 @@ async function cloneSwiftDocCRender(buildDirectory: string): Promise<string> {
5757
return swiftDocCRenderDirectory;
5858
}
5959

60-
async function exec(
61-
command: string,
62-
args: string[],
63-
options: { cwd?: string; env?: { [key: string]: string } } = {}
64-
): Promise<void> {
65-
let logMessage = "> " + command;
66-
if (args.length > 0) {
67-
logMessage += " " + args.join(" ");
68-
}
69-
console.log(logMessage + "\n");
70-
return new Promise<void>((resolve, reject) => {
71-
const childProcess = spawn(command, args, { stdio: "inherit", ...options });
72-
childProcess.once("error", reject);
73-
childProcess.once("close", (code, signal) => {
74-
if (signal !== null) {
75-
reject(new Error(`Process exited due to signal '${signal}'`));
76-
} else if (code !== 0) {
77-
reject(new Error(`Process exited with code ${code}`));
78-
} else {
79-
resolve();
80-
}
81-
console.log("");
82-
});
83-
});
84-
}
85-
86-
(async () => {
87-
const outputDirectory = path.join(__dirname, "..", "assets", "swift-docc-render");
60+
main(async () => {
61+
const outputDirectory = path.join(getRootDirectory(), "assets", "swift-docc-render");
8862
if (process.argv.includes("postinstall")) {
8963
try {
9064
await stat(outputDirectory);
@@ -114,7 +88,4 @@ async function exec(
11488
console.error(error);
11589
});
11690
}
117-
})().catch(error => {
118-
console.error(error);
119-
process.exit(1);
12091
});

0 commit comments

Comments
 (0)