Skip to content

Commit

Permalink
Merge pull request #5 from daddykotex/dfrancoeur/download-coursier
Browse files Browse the repository at this point in the history
Setup coursier w/o embedding in the extension
  • Loading branch information
Baccata authored Feb 24, 2022
2 parents e4ce799 + 27ae236 commit 0f18c9b
Show file tree
Hide file tree
Showing 16 changed files with 2,605 additions and 151 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
yarn.lock linguist-generated=true
8 changes: 1 addition & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,7 @@ jobs:
- uses: actions/checkout@v2
- uses: borales/[email protected]
with:
cmd: install
- uses: borales/[email protected]
with:
cmd: format-check
- uses: borales/[email protected]
with:
cmd: build
cmd: ci

deploy:
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v'))
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,23 @@ https://github.com/disneystreaming/smithy-language-server/
* Basic auto-completion
* Simple grammar for Smithy syntax highlighting


## Smithy build

You can configure your Smithy projects using a Smithy build file.

The name of the file is `smithy-build.json`. This build file is a JSON file has the following structure:

- imports: `Array[String]` - a list of path to directories or file to include in the Smithy model - default: `["$PWD"]`
- mavenDependencies: `Array[String]` - maven dependencies that contains Smithy definitions - default: `[]`
- mavenRepositories: `Array[String]` - maven repositories to fetch mavenDependencies from - default: `m2local` and maven central

An example Smithy build file looks like this:

```json
{
"imports": ["./specs"],
"mavenDependencies": ["com.disneystreaming.smithy4s:smithy4s-protocol_2.13:latest.stable"],
"mavenRepositories": ["https://full.url.to.your.mavenrepository.tld"]
}
```
Binary file removed coursier
Binary file not shown.
14 changes: 14 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
preset: "ts-jest/presets/default-esm", // or other ESM presets
globals: {
"ts-jest": {
useESM: true,
},
},
moduleNameMapper: {
"^(\\.{1,2}/.*)\\.js$": "$1",
},
};
15 changes: 11 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,13 @@
"prepublishOnly": "vsce package -o extension.vsix",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"test": "yarn compile",
"clean": "rm -rf node_modules/ out/",
"test": "jest",
"test:watch": "yarn run test --watch",
"build": "vsce package --yarn",
"format": "prettier --write '**/*.{ts,js,json,yml}'",
"format-check": "prettier --check '**/*.{ts,js,json,yml}'"
"format-check": "prettier --check '**/*.{ts,js,json,yml}'",
"ci": "yarn clean && yarn install --no-progress && yarn format-check && yarn build && yarn test"
},
"files": [
"extension.vsix"
Expand All @@ -81,16 +84,20 @@
"vscode": "^1.43.0"
},
"dependencies": {
"follow-redirects": "^1.14.9",
"vscode-languageclient": "^6.1.3"
},
"devDependencies": {
"@types/mocha": "^8.0.3",
"@types/node": "^12.12.0",
"@types/follow-redirects": "^1.14.1",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.21",
"@types/vscode": "1.43.0",
"@typescript-eslint/parser": "^2.3.0",
"eslint": "^6.4.0",
"jest": "^27.5.1",
"mocha": "^8.1.1",
"prettier": "^2.5.1",
"ts-jest": "^27.1.3",
"typescript": "^4.0.2",
"vsce": "^1.87.1",
"vscode-test": "^1.3.0"
Expand Down
12 changes: 12 additions & 0 deletions src/coursier/coursier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { downloadCoursierIfRequired } from "./download-coursier";
import { findCoursierOnPath } from "./path-check";

export function getCoursierExecutable(extensionPath: string): Promise<string> {
return findCoursierOnPath(extensionPath).then((paths) => {
if (paths.length > 0) {
return paths[0];
} else {
return downloadCoursierIfRequired(extensionPath, "v2.0.6");
}
});
}
99 changes: 99 additions & 0 deletions src/coursier/download-coursier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import * as path from "path";
import { https } from "follow-redirects";
import { IncomingMessage } from "http";
import * as fs from "fs";
import { access, mkdir } from "fs/promises";

export function downloadCoursierIfRequired(
extensionPath: string,
versionPath: string
): Promise<string> {
function binPath(filename: string) {
return path.join(extensionPath, filename);
}

function createDir() {
return mkdir(extensionPath).catch((err: { code?: string }) => {
return err && err.code === "EEXIST"
? Promise.resolve()
: Promise.reject(err);
});
}

const urls = {
darwin: `https://github.com/coursier/coursier/releases/download/${versionPath}/cs-x86_64-apple-darwin`,
linux: `https://github.com/coursier/coursier/releases/download/${versionPath}/cs-x86_64-pc-linux`,
win32: `https://github.com/coursier/coursier/releases/download/${versionPath}/cs-x86_64-pc-win32.exe`,
};
const targets = {
darwin: binPath("coursier"),
linux: binPath("coursier"),
win32: binPath("coursier.exe"),
};

const targetFile = targets[process.platform];
return validBinFileExists(targetFile).then((valid) => {
return valid
? targetFile
: createDir().then(() =>
downloadFile(urls[process.platform], targetFile)
);
});
}

function validBinFileExists(file: string): Promise<boolean> {
return access(file, fs.constants.X_OK)
.then(() => true)
.catch(() => false);
}

function downloadFile(url: string, targetFile: string): Promise<string> {
function promiseGet(url: string): Promise<IncomingMessage> {
return new Promise((resolve, reject) => {
https.get(url, (response) => {
if (response.statusCode === 200) {
resolve(response);
} else {
reject(
new Error(
`Server responded with ${response.statusCode}: ${response.statusMessage}`
)
);
}
});
});
}

function writeToDisk(response: IncomingMessage): Promise<string> {
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(targetFile, {
flags: "wx",
mode: 0o755,
});
response.pipe(file);

file.on("finish", () => {
console.log(`Finished downloaded file at ${targetFile}`);
resolve(targetFile);
});

file.on("error", (err: { code: string | undefined }) => {
if (file) {
file.close();
fs.unlink(targetFile, () => {}); // Delete temp file
}

if (err.code === "EEXIST") {
console.log(`File already exists at ${targetFile}`);
resolve(targetFile);
} else {
console.error(`File error while downloading file at ${targetFile}`);
console.error(err);
reject(err);
}
});
});
}
// adapted from https://stackoverflow.com/a/45007624
return promiseGet(url).then((resp) => writeToDisk(resp));
}
45 changes: 45 additions & 0 deletions src/coursier/path-check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { spawn } from "child_process";

/**
* This type is used to bypass the `defaultImpl` when running the tests.
*/
export type ExecForCode = {
run: (execName: string, args: Array<string>, cwd: string) => Promise<number>;
};

const defaultImpl: ExecForCode = {
run: (execName: string, args: Array<string>, cwd: string) => {
console.log("real");
return new Promise((resolve, reject) => {
const options = { cwd };
const resolveProcess = spawn(execName, args, options);
resolveProcess.on("exit", (exitCode) => {
resolve(exitCode);
});
resolveProcess.on("error", (err) => {
reject(err);
});
});
},
};

export function findCoursierOnPath(
cwd: string,
execForCode: ExecForCode = defaultImpl
): Promise<Array<string>> {
function availableOnPath(execName: string): Promise<boolean> {
return execForCode
.run(execName, ["--help"], cwd)
.then((ec) => ec === 0)
.catch(() => false);
}

const possibleCoursierNames = ["cs", "coursier"];
return possibleCoursierNames.reduce((accP, current) => {
return accP.then((acc) => {
return availableOnPath(current).then((succeeeded) => {
return succeeeded ? [...acc, current] : acc;
});
});
}, Promise.resolve([]));
}
Loading

0 comments on commit 0f18c9b

Please sign in to comment.