Skip to content
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

Implement branch list using callbacks from exec function #1045

Merged
merged 3 commits into from Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 80 additions & 0 deletions __test__/git-command-manager.test.ts
@@ -0,0 +1,80 @@
import * as exec from '@actions/exec'
import * as fshelper from '../lib/fs-helper'
import * as commandManager from '../lib/git-command-manager'

let git: commandManager.IGitCommandManager
let mockExec = jest.fn()

describe('git-auth-helper tests', () => {
beforeAll(async () => {})

beforeEach(async () => {
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
})

afterEach(() => {
jest.restoreAllMocks()
})

afterAll(() => {})

it('branch list matches', async () => {
mockExec.mockImplementation((path, args, options) => {
console.log(args, options.listeners.stdout)

if (args.includes('version')) {
options.listeners.stdout(Buffer.from('2.18'))
return 0
}

if (args.includes('rev-parse')) {
options.listeners.stdline(Buffer.from('refs/heads/foo'))
options.listeners.stdline(Buffer.from('refs/heads/bar'))
return 0
}

return 1
})
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
const workingDirectory = 'test'
const lfs = false
git = await commandManager.createCommandManager(workingDirectory, lfs)

let branches = await git.branchList(false)

expect(branches).toHaveLength(2)
expect(branches.sort()).toEqual(['foo', 'bar'].sort())
})

it('ambiguous ref name output is captured', async () => {
mockExec.mockImplementation((path, args, options) => {
console.log(args, options.listeners.stdout)

if (args.includes('version')) {
options.listeners.stdout(Buffer.from('2.18'))
return 0
}

if (args.includes('rev-parse')) {
options.listeners.stdline(Buffer.from('refs/heads/foo'))
// If refs/tags/v1 and refs/heads/tags/v1 existed on this repository
options.listeners.errline(
Buffer.from("error: refname 'tags/v1' is ambiguous")
)
return 0
}

return 1
})
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
const workingDirectory = 'test'
const lfs = false
git = await commandManager.createCommandManager(workingDirectory, lfs)

let branches = await git.branchList(false)

expect(branches).toHaveLength(1)
expect(branches.sort()).toEqual(['foo'].sort())
})
})
66 changes: 48 additions & 18 deletions dist/index.js
Expand Up @@ -7441,27 +7441,53 @@ class GitCommandManager {
const result = [];
// Note, this implementation uses "rev-parse --symbolic-full-name" because the output from
// "branch --list" is more difficult when in a detached HEAD state.
// Note, this implementation uses "rev-parse --symbolic-full-name" because there is a bug
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names.
// TODO(https://github.com/actions/checkout/issues/786): this implementation uses
// "rev-parse --symbolic-full-name" because there is a bug
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names. When
// 2.18 is no longer supported, we can switch back to --symbolic.
const args = ['rev-parse', '--symbolic-full-name'];
if (remote) {
args.push('--remotes=origin');
}
else {
args.push('--branches');
}
const output = yield this.execGit(args);
for (let branch of output.stdout.trim().split('\n')) {
const stderr = [];
const errline = [];
const stdout = [];
const stdline = [];
const listeners = {
stderr: (data) => {
stderr.push(data.toString());
},
errline: (data) => {
errline.push(data.toString());
},
stdout: (data) => {
stdout.push(data.toString());
},
stdline: (data) => {
stdline.push(data.toString());
}
};
// Suppress the output in order to avoid flooding annotations with innocuous errors.
yield this.execGit(args, false, true, listeners);
core.debug(`stderr callback is: ${stderr}`);
core.debug(`errline callback is: ${errline}`);
core.debug(`stdout callback is: ${stdout}`);
core.debug(`stdline callback is: ${stdline}`);
for (let branch of stdline) {
branch = branch.trim();
if (branch) {
if (branch.startsWith('refs/heads/')) {
branch = branch.substr('refs/heads/'.length);
}
else if (branch.startsWith('refs/remotes/')) {
branch = branch.substr('refs/remotes/'.length);
}
result.push(branch);
if (!branch) {

Choose a reason for hiding this comment

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

nice!

continue;
}
if (branch.startsWith('refs/heads/')) {
branch = branch.substring('refs/heads/'.length);
}
else if (branch.startsWith('refs/remotes/')) {
branch = branch.substring('refs/remotes/'.length);
}
result.push(branch);
}
return result;
});
Expand Down Expand Up @@ -7712,7 +7738,7 @@ class GitCommandManager {
return result;
});
}
execGit(args, allowAllExitCodes = false, silent = false) {
execGit(args, allowAllExitCodes = false, silent = false, customListeners = {}) {
return __awaiter(this, void 0, void 0, function* () {
fshelper.directoryExistsSync(this.workingDirectory, true);
const result = new GitOutput();
Expand All @@ -7723,20 +7749,24 @@ class GitCommandManager {
for (const key of Object.keys(this.gitEnv)) {
env[key] = this.gitEnv[key];
}
const defaultListener = {
stdout: (data) => {
stdout.push(data.toString());
}
};
const mergedListeners = Object.assign(Object.assign({}, defaultListener), customListeners);
const stdout = [];
const options = {
cwd: this.workingDirectory,
env,
silent,
ignoreReturnCode: allowAllExitCodes,
listeners: {
stdout: (data) => {
stdout.push(data.toString());
}
}
listeners: mergedListeners
};
result.exitCode = yield exec.exec(`"${this.gitPath}"`, args, options);
result.stdout = stdout.join('');
core.debug(result.exitCode.toString());
core.debug(result.stdout);
return result;
});
}
Expand Down
75 changes: 57 additions & 18 deletions src/git-command-manager.ts
Expand Up @@ -94,8 +94,11 @@ class GitCommandManager {

// Note, this implementation uses "rev-parse --symbolic-full-name" because the output from
// "branch --list" is more difficult when in a detached HEAD state.
// Note, this implementation uses "rev-parse --symbolic-full-name" because there is a bug
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names.

// TODO(https://github.com/actions/checkout/issues/786): this implementation uses
// "rev-parse --symbolic-full-name" because there is a bug
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names. When
// 2.18 is no longer supported, we can switch back to --symbolic.

const args = ['rev-parse', '--symbolic-full-name']
if (remote) {
Expand All @@ -104,19 +107,47 @@ class GitCommandManager {
args.push('--branches')
}

const output = await this.execGit(args)
const stderr: string[] = []
const errline: string[] = []
const stdout: string[] = []
const stdline: string[] = []

const listeners = {
stderr: (data: Buffer) => {
stderr.push(data.toString())
},
errline: (data: Buffer) => {
errline.push(data.toString())
},
stdout: (data: Buffer) => {
stdout.push(data.toString())
},
stdline: (data: Buffer) => {
stdline.push(data.toString())
}
}

for (let branch of output.stdout.trim().split('\n')) {
// Suppress the output in order to avoid flooding annotations with innocuous errors.
await this.execGit(args, false, true, listeners)

core.debug(`stderr callback is: ${stderr}`)
core.debug(`errline callback is: ${errline}`)
core.debug(`stdout callback is: ${stdout}`)
core.debug(`stdline callback is: ${stdline}`)

for (let branch of stdline) {
branch = branch.trim()
if (branch) {
if (branch.startsWith('refs/heads/')) {
branch = branch.substr('refs/heads/'.length)
} else if (branch.startsWith('refs/remotes/')) {
branch = branch.substr('refs/remotes/'.length)
}
if (!branch) {
continue
}

result.push(branch)
if (branch.startsWith('refs/heads/')) {
branch = branch.substring('refs/heads/'.length)
} else if (branch.startsWith('refs/remotes/')) {
branch = branch.substring('refs/remotes/'.length)
}

result.push(branch)
}

return result
Expand Down Expand Up @@ -395,7 +426,8 @@ class GitCommandManager {
private async execGit(
args: string[],
allowAllExitCodes = false,
silent = false
silent = false,
customListeners = {}
): Promise<GitOutput> {
fshelper.directoryExistsSync(this.workingDirectory, true)

Expand All @@ -409,22 +441,29 @@ class GitCommandManager {
env[key] = this.gitEnv[key]
}

const stdout: string[] = []
const defaultListener = {
stdout: (data: Buffer) => {
stdout.push(data.toString())
}
}

const mergedListeners = {...defaultListener, ...customListeners}

const stdout: string[] = []
const options = {
cwd: this.workingDirectory,
env,
silent,
ignoreReturnCode: allowAllExitCodes,
listeners: {
stdout: (data: Buffer) => {
stdout.push(data.toString())
}
}
listeners: mergedListeners
}

result.exitCode = await exec.exec(`"${this.gitPath}"`, args, options)
result.stdout = stdout.join('')

core.debug(result.exitCode.toString())
core.debug(result.stdout)

return result
}

Expand Down