Skip to content

Use latest release from sqlcmd #229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
34 changes: 33 additions & 1 deletion __tests__/Setup.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as core from "@actions/core";
import { HttpClient } from "@actions/http-client";
import * as tc from "@actions/tool-cache";
import Setup from "../src/Setup";
import Setup, { sqlcmdDownloadUrl, sqlcmdFallbackVersion, sqlcmdToolName } from "../src/Setup";

jest.mock('@actions/core');

Expand All @@ -10,6 +11,7 @@ describe('Setup.ts tests', () => {
})

it('sets up sqlcmd correctly', async() => {
const extractVersionSpy = jest.spyOn(Setup, 'extractVersionFromLatestRelease').mockResolvedValue('1.1.1');
const cacheLookupSpy = jest.spyOn(tc, 'find').mockReturnValue('');
const downloadToolSpy = jest.spyOn(tc, 'downloadTool').mockResolvedValue('');
const extractTarSpy = jest.spyOn(tc, 'extractTar').mockResolvedValue('');
Expand All @@ -19,6 +21,7 @@ describe('Setup.ts tests', () => {

await Setup.setupSqlcmd();

expect(extractVersionSpy).toHaveBeenCalled();
expect(cacheLookupSpy).toHaveBeenCalled();
expect(downloadToolSpy).toHaveBeenCalled();
if (process.platform === 'win32') {
Expand All @@ -30,4 +33,33 @@ describe('Setup.ts tests', () => {
expect(addPathSpy).toHaveBeenCalled();
expect(cacheDirSpy).toHaveBeenCalled();
});

it('gets the version number from the latest release', async() => {
const headSpy = jest.spyOn(HttpClient.prototype, 'head').mockResolvedValue({
message: {
headers: {
location: 'https://github.com/microsoft/go-sqlcmd/releases/tag/v0.100.100'
}
}
} as any);

const version = await Setup.extractVersionFromLatestRelease(sqlcmdDownloadUrl);

expect(headSpy).toHaveBeenCalled();
expect(version).toBe('0.100.100');
});

it('uses fallback version when latest version cannot be determined', async() => {
const headSpy = jest.spyOn(HttpClient.prototype, 'head').mockResolvedValue({
message: {
headers: {}
}
} as any);
const cacheLookupSpy = jest.spyOn(tc, 'find').mockReturnValue('fake_path');

await Setup.setupSqlcmd();

expect(headSpy).toHaveBeenCalled();
expect(cacheLookupSpy).toHaveBeenCalledWith(sqlcmdToolName, sqlcmdFallbackVersion);
});
})
2 changes: 1 addition & 1 deletion lib/main.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions lib/main.js.LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ PERFORMANCE OF THIS SOFTWARE.

/*! @azure/msal-common v6.3.0 2022-05-02 */

/*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */

/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! ws. MIT License. Einar Otto Stangvik <[email protected]> */

/**
* @copyright (c) 2016, Philipp Thürwächter & Pattrick Hüper
* @copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
Expand Down
54 changes: 46 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/exec": "^1.0.1",
"@actions/http-client": "2.2.1",
"@actions/tool-cache": "^2.0.1",
"azure-actions-webclient": "^1.0.3",
"glob": "^10.3.12",
Expand Down
35 changes: 32 additions & 3 deletions src/Setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,64 @@
// https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runspre

import * as core from '@actions/core';
import { HttpClient } from '@actions/http-client';
import * as tc from '@actions/tool-cache';
import * as path from 'path';
import uuidV4 from 'uuid/v4';

export const sqlcmdToolName = 'go-sqlcmd';
export const sqlcmdVersion = '1.0.0';
export const sqlcmdDownloadUrl = 'https://github.com/microsoft/go-sqlcmd/releases/latest';
export const sqlcmdFallbackVersion = '1.6.0';

export default class Setup {

/**
* Ensures go-sqlcmd is in the runner's tool cache and PATH environment variable.
*/
public static async setupSqlcmd(): Promise<void> {

// Determine latest version or use fallback version
let sqlcmdVersion = await this.extractVersionFromLatestRelease(sqlcmdDownloadUrl);
if (sqlcmdVersion) {
core.debug(`Using latest go-sqlcmd version: ${sqlcmdVersion}`);
} else {
sqlcmdVersion = sqlcmdFallbackVersion;
core.debug(`Failed to get latest go-sqlcmd version. Using fallback version: ${sqlcmdVersion}`);
}

// Get sqlcmd from tool cache; if not found, download it and add to tool cache
let sqlcmdPath = tc.find(sqlcmdToolName, sqlcmdVersion);
if (!sqlcmdPath) {
const extractedPath = await this.downloadAndExtractSqlcmd();
const extractedPath = await this.downloadAndExtractSqlcmd(sqlcmdVersion);
sqlcmdPath = await tc.cacheDir(extractedPath, sqlcmdToolName, sqlcmdVersion);
}

// Add sqlcmd to PATH
core.addPath(sqlcmdPath);
}

/**
* Gets the version number from the latest release based on the redirected URL.
* @returns The version number or undefined if the version could not be parsed or HTTP request failed.
*/
public static async extractVersionFromLatestRelease(releaseUrl: string): Promise<string | undefined> {
const http = new HttpClient('Azure/sql-action', undefined, { allowRedirects: false });
Copy link
Contributor

@chlafreniere chlafreniere Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have access to user token (or some sort of system token) from sql-action here? Since this is unauthenticated, it will be much more rate-limited.

If we do, may be easier just to use octokit library instead of httpclient

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately it doesn't appear there's an easy way to get the token, we'd have to introduce a new input so users can pass in GITHUB_TOKEN to.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the rate limiting will be here - since we're not using a REST API necessarily, we're checking the redirect.

const response = await http.head(releaseUrl);
// Should be redirected to something like https://github.com/microsoft/go-sqlcmd/releases/tag/v1.6.0
const location = response.message.headers.location;
if (location) {
// Return the last bit with v prefix removed
return location.split('/').pop()?.slice(1);
}

return undefined;
}

/**
* Downloads go-sqlcmd release from GitHub and extracts from the compressed file.
* @returns The path to the extracted file.
*/
private static async downloadAndExtractSqlcmd(): Promise<string> {
private static async downloadAndExtractSqlcmd(sqlcmdVersion: string): Promise<string> {
let downloadPath: string;
switch (process.platform) {
case 'linux':
Expand Down
Loading