git: alternative github context and additional methods
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
@@ -17,18 +17,131 @@
|
||||
import {beforeEach, describe, expect, it, jest} from '@jest/globals';
|
||||
|
||||
import {Git} from '../src/git';
|
||||
import {Exec} from '../src/exec';
|
||||
import {ExecOutput} from '@actions/exec';
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('git', () => {
|
||||
it('returns git remote ref', async () => {
|
||||
try {
|
||||
expect(await Git.getRemoteSha('https://github.com/docker/buildx.git', 'refs/pull/648/head')).toEqual('f11797113e5a9b86bd976329c5dbb8a8bfdfadfa');
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(e).toEqual(null);
|
||||
}
|
||||
describe('context', () => {
|
||||
it('returns mocked ref and sha', async () => {
|
||||
jest.spyOn(Exec, 'getExecOutput').mockImplementation((cmd, args): Promise<ExecOutput> => {
|
||||
const fullCmd = `${cmd} ${args?.join(' ')}`;
|
||||
let result = '';
|
||||
switch (fullCmd) {
|
||||
case 'git show --format=%H HEAD --quiet --':
|
||||
result = 'test-sha';
|
||||
break;
|
||||
case 'git symbolic-ref HEAD':
|
||||
result = 'refs/heads/test';
|
||||
break;
|
||||
}
|
||||
return Promise.resolve({
|
||||
stdout: result,
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
});
|
||||
});
|
||||
const ctx = await Git.context();
|
||||
expect(ctx.ref).toEqual('refs/heads/test');
|
||||
expect(ctx.sha).toEqual('test-sha');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isInsideWorkTree', () => {
|
||||
it('have been called', async () => {
|
||||
const execSpy = jest.spyOn(Exec, 'getExecOutput');
|
||||
try {
|
||||
await Git.isInsideWorkTree();
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
expect(execSpy).toHaveBeenCalledWith(`git`, ['rev-parse', '--is-inside-work-tree'], {
|
||||
silent: true,
|
||||
ignoreReturnCode: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('remoteSha', () => {
|
||||
it('returns git remote sha', async () => {
|
||||
expect(await Git.remoteSha('https://github.com/docker/buildx.git', 'refs/pull/648/head')).toEqual('f11797113e5a9b86bd976329c5dbb8a8bfdfadfa');
|
||||
});
|
||||
});
|
||||
|
||||
describe('remoteURL', () => {
|
||||
it('have been called', async () => {
|
||||
const execSpy = jest.spyOn(Exec, 'getExecOutput');
|
||||
try {
|
||||
await Git.remoteURL();
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
expect(execSpy).toHaveBeenCalledWith(`git`, ['remote', 'get-url', 'origin'], {
|
||||
silent: true,
|
||||
ignoreReturnCode: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ref', () => {
|
||||
it('have been called', async () => {
|
||||
const execSpy = jest.spyOn(Exec, 'getExecOutput');
|
||||
try {
|
||||
await Git.ref();
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
expect(execSpy).toHaveBeenCalledWith(`git`, ['symbolic-ref', 'HEAD'], {
|
||||
silent: true,
|
||||
ignoreReturnCode: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fullCommit', () => {
|
||||
it('have been called', async () => {
|
||||
const execSpy = jest.spyOn(Exec, 'getExecOutput');
|
||||
try {
|
||||
await Git.fullCommit();
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
expect(execSpy).toHaveBeenCalledWith(`git`, ['show', '--format=%H', 'HEAD', '--quiet', '--'], {
|
||||
silent: true,
|
||||
ignoreReturnCode: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('shortCommit', () => {
|
||||
it('have been called', async () => {
|
||||
const execSpy = jest.spyOn(Exec, 'getExecOutput');
|
||||
try {
|
||||
await Git.shortCommit();
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
expect(execSpy).toHaveBeenCalledWith(`git`, ['show', '--format=%h', 'HEAD', '--quiet', '--'], {
|
||||
silent: true,
|
||||
ignoreReturnCode: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('tag', () => {
|
||||
it('have been called', async () => {
|
||||
const execSpy = jest.spyOn(Exec, 'getExecOutput');
|
||||
try {
|
||||
await Git.tag();
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
expect(execSpy).toHaveBeenCalledWith(`git`, ['tag', '--points-at', 'HEAD', '--sort', '-version:creatordate'], {
|
||||
silent: true,
|
||||
ignoreReturnCode: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -73,7 +73,7 @@ export class Install {
|
||||
if (ref.match(/^[0-9a-fA-F]{40}$/)) {
|
||||
vspec = ref;
|
||||
} else {
|
||||
vspec = await Git.getRemoteSha(repo, ref);
|
||||
vspec = await Git.remoteSha(repo, ref);
|
||||
}
|
||||
core.debug(`Install.build: tool version spec ${vspec}`);
|
||||
|
||||
|
||||
78
src/git.ts
78
src/git.ts
@@ -15,21 +15,81 @@
|
||||
*/
|
||||
|
||||
import {Exec} from './exec';
|
||||
import {Context} from '@actions/github/lib/context';
|
||||
import {Context as GitContext} from './types/git';
|
||||
|
||||
export class Git {
|
||||
public static async getRemoteSha(repo: string, ref: string): Promise<string> {
|
||||
return await Exec.getExecOutput(`git`, ['ls-remote', repo, ref], {
|
||||
ignoreReturnCode: true,
|
||||
silent: true
|
||||
}).then(res => {
|
||||
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||
throw new Error(res.stderr);
|
||||
}
|
||||
const [rsha] = res.stdout.trim().split(/[\s\t]/);
|
||||
public static async context(): Promise<GitContext> {
|
||||
const ctx = new Context();
|
||||
ctx.ref = await Git.ref();
|
||||
ctx.sha = await Git.fullCommit();
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public static async isInsideWorkTree(): Promise<boolean> {
|
||||
return await Git.exec(['rev-parse', '--is-inside-work-tree'])
|
||||
.then(out => {
|
||||
return out === 'true';
|
||||
})
|
||||
.catch(() => {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public static async remoteSha(repo: string, ref: string): Promise<string> {
|
||||
return await Git.exec(['ls-remote', repo, ref]).then(out => {
|
||||
const [rsha] = out.split(/[\s\t]/);
|
||||
if (rsha.length == 0) {
|
||||
throw new Error(`Cannot find remote ref for ${repo}#${ref}`);
|
||||
}
|
||||
return rsha;
|
||||
});
|
||||
}
|
||||
|
||||
public static async remoteURL(): Promise<string> {
|
||||
return await Git.exec(['remote', 'get-url', 'origin']).then(rurl => {
|
||||
if (rurl.length == 0) {
|
||||
return Git.exec(['remote', 'get-url', 'upstream']).then(rurl => {
|
||||
if (rurl.length == 0) {
|
||||
throw new Error(`Cannot find remote URL for origin or upstream`);
|
||||
}
|
||||
return rurl;
|
||||
});
|
||||
}
|
||||
return rurl;
|
||||
});
|
||||
}
|
||||
|
||||
public static async ref(): Promise<string> {
|
||||
return await Git.exec(['symbolic-ref', 'HEAD']);
|
||||
}
|
||||
|
||||
public static async fullCommit(): Promise<string> {
|
||||
return await Git.exec(['show', '--format=%H', 'HEAD', '--quiet', '--']);
|
||||
}
|
||||
|
||||
public static async shortCommit(): Promise<string> {
|
||||
return await Git.exec(['show', '--format=%h', 'HEAD', '--quiet', '--']);
|
||||
}
|
||||
|
||||
public static async tag(): Promise<string> {
|
||||
return await Git.exec(['tag', '--points-at', 'HEAD', '--sort', '-version:creatordate']).then(tags => {
|
||||
if (tags.length == 0) {
|
||||
return Git.exec(['describe', '--tags', '--abbrev=0']);
|
||||
}
|
||||
return tags.split('\n')[0];
|
||||
});
|
||||
}
|
||||
|
||||
private static async exec(args: string[] = []): Promise<string> {
|
||||
return await Exec.getExecOutput(`git`, args, {
|
||||
ignoreReturnCode: true,
|
||||
silent: true
|
||||
}).then(res => {
|
||||
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||
throw new Error(res.stderr);
|
||||
}
|
||||
return res.stdout.trim();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
19
src/types/git.ts
Normal file
19
src/types/git.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright 2023 actions-toolkit authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {Context as GitHubContext} from '@actions/github/lib/context';
|
||||
|
||||
export type Context = GitHubContext;
|
||||
Reference in New Issue
Block a user