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

feat: add --skip-remote-check option #2426

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Usage:
.option('success', {...stringList, group: 'Plugins'})
.option('fail', {...stringList, group: 'Plugins'})
.option('debug', {describe: 'Output debugging information', type: 'boolean', group: 'Options'})
.option('skip-remote-check', {describe: 'TODO', type: 'boolean', group: 'Options'})
.option('d', {alias: 'dry-run', describe: 'Skip publishing', type: 'boolean', group: 'Options'})
.option('h', {alias: 'help', group: 'Options'})
.option('v', {alias: 'version', group: 'Options'})
Expand Down
39 changes: 24 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ async function run(context, plugins) {
return false;
}

if (options.skipRemoteCheck && !options.dryRun) {
logger.error('--skip-remote-check can only be specified in dry run mode.');
return false;
}

// Verify config
await verify(context);

Expand All @@ -80,25 +85,29 @@ async function run(context, plugins) {
}`
);

try {
if (options.skipRemoteCheck) {
logger.warn('Skipping up-to-date check for branch %s because skip-remote-check is set', context.branch.name);
} else {
try {
await verifyAuth(options.repositoryUrl, context.branch.name, {cwd, env});
} catch (error) {
if (!(await isBranchUpToDate(options.repositoryUrl, context.branch.name, {cwd, env}))) {
logger.log(
`The local branch ${context.branch.name} is behind the remote one, therefore a new version won't be published.`
);
return false;
try {
await verifyAuth(options.repositoryUrl, context.branch.name, {cwd, env});
} catch (error) {
if (!(await isBranchUpToDate(options.repositoryUrl, context.branch.name, {cwd, env}))) {
logger.log(
`The local branch ${context.branch.name} is behind the remote one, therefore a new version won't be published.`
);
return false;
}

throw error;
}

throw error;
} catch (error) {
logger.error(`The command "${error.command}" failed with the error message ${error.stderr}.`);
throw getError('EGITNOPERMISSION', context);
}
} catch (error) {
logger.error(`The command "${error.command}" failed with the error message ${error.stderr}.`);
throw getError('EGITNOPERMISSION', context);
}

logger.success(`Allowed to push to the Git repository`);
logger.success(`Allowed to push to the Git repository`);
}

await plugins.verifyConditions(context);

Expand Down
56 changes: 30 additions & 26 deletions lib/get-git-auth-url.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ module.exports = async (context) => {
BITBUCKET_TOKEN_BASIC_AUTH: '',
};

let {repositoryUrl} = context.options;
let {repositoryUrl, skipRemoteCheck} = context.options;
const info = hostedGitInfo.fromUrl(repositoryUrl, {noGitPlus: true});
const {protocol, ...parsed} = parse(repositoryUrl);

Expand All @@ -85,35 +85,39 @@ module.exports = async (context) => {
repositoryUrl = format({...parsed, protocol: protocol.includes('https') ? 'https' : 'http', href: null});
}

// Test if push is allowed without transforming the URL (e.g. is ssh keys are set up)
try {
debug('Verifying ssh auth by attempting to push to %s', repositoryUrl);
await verifyAuth(repositoryUrl, branch.name, {cwd, env});
} catch {
debug('SSH key auth failed, falling back to https.');
const envVars = Object.keys(GIT_TOKENS).filter((envVar) => !isNil(env[envVar]));
if (skipRemoteCheck) {
debug('Skipping remote verification, blindly using url %s', repositoryUrl);
} else {
// Test if push is allowed without transforming the URL (e.g. is ssh keys are set up)
try {
debug('Verifying ssh auth by attempting to push to %s', repositoryUrl);
await verifyAuth(repositoryUrl, branch.name, {cwd, env});
} catch {
debug('SSH key auth failed, falling back to https.');
const envVars = Object.keys(GIT_TOKENS).filter((envVar) => !isNil(env[envVar]));

// Skip verification if there is no ambiguity on which env var to use for authentication
if (envVars.length === 1) {
const gitCredentials = `${GIT_TOKENS[envVars[0]] || ''}${env[envVars[0]]}`;
return formatAuthUrl(protocol, repositoryUrl, gitCredentials);
}
// Skip verification if there is no ambiguity on which env var to use for authentication
if (envVars.length === 1) {
const gitCredentials = `${GIT_TOKENS[envVars[0]] || ''}${env[envVars[0]]}`;
return formatAuthUrl(protocol, repositoryUrl, gitCredentials);
}

if (envVars.length > 1) {
debug(`Found ${envVars.length} credentials in environment, trying all of them`);
if (envVars.length > 1) {
debug(`Found ${envVars.length} credentials in environment, trying all of them`);

const candidateRepositoryUrls = [];
for (const envVar of envVars) {
const gitCredentials = `${GIT_TOKENS[envVar] || ''}${env[envVar]}`;
const authUrl = formatAuthUrl(protocol, repositoryUrl, gitCredentials);
candidateRepositoryUrls.push(ensureValidAuthUrl(context, authUrl));
}
const candidateRepositoryUrls = [];
for (const envVar of envVars) {
const gitCredentials = `${GIT_TOKENS[envVar] || ''}${env[envVar]}`;
const authUrl = formatAuthUrl(protocol, repositoryUrl, gitCredentials);
candidateRepositoryUrls.push(ensureValidAuthUrl(context, authUrl));
}

const validRepositoryUrls = await Promise.all(candidateRepositoryUrls);
const chosenAuthUrlIndex = validRepositoryUrls.findIndex((url) => url !== null);
if (chosenAuthUrlIndex > -1) {
debug(`Using "${envVars[chosenAuthUrlIndex]}" to authenticate`);
return validRepositoryUrls[chosenAuthUrlIndex];
const validRepositoryUrls = await Promise.all(candidateRepositoryUrls);
const chosenAuthUrlIndex = validRepositoryUrls.findIndex((url) => url !== null);
if (chosenAuthUrlIndex > -1) {
debug(`Using "${envVars[chosenAuthUrlIndex]}" to authenticate`);
return validRepositoryUrls[chosenAuthUrlIndex];
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions test/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ test.serial('Pass options to semantic-release API', async (t) => {
'fail2',
'--debug',
'-d',
'--skip-remote-check',
];
const cli = proxyquire('../cli', {'.': run, process: {...process, argv}});

Expand All @@ -82,6 +83,7 @@ test.serial('Pass options to semantic-release API', async (t) => {
t.deepEqual(run.args[0][0].fail, ['fail1', 'fail2']);
t.is(run.args[0][0].debug, true);
t.is(run.args[0][0].dryRun, true);
t.is(run.args[0][0].skipRemoteCheck, true);

t.is(exitCode, 0);
});
Expand Down
5 changes: 5 additions & 0 deletions test/get-git-auth-url.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,8 @@ test('Do not add git credential to repositoryUrl if push is allowed', async (t)
repositoryUrl
);
});

test('Do not verify the url if skipRemoteCheck is set', (t) => {
// TODO: Implement this test, somehow.
t.true(true);
});
25 changes: 25 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,31 @@ test('Returns false if triggered by a PR', async (t) => {
);
});

test('Does not allow specifying skipRemoteCheck without dryRun', async (t) => {
const {cwd, repositoryUrl} = await gitRepo(true);

const semanticRelease = requireNoCache('..', {
'./lib/get-logger': () => t.context.logger,
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});

t.false(
await semanticRelease(
{cwd, repositoryUrl, skipRemoteCheck: true},
{cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()}
)
);
t.is(
t.context.error.args[t.context.error.args.length - 1][0],
'--skip-remote-check can only be specified in dry run mode.'
);
});

test("Doesn't call verifyAuth when skipRemoteCheck is set", (t) => {
// TODO: Implement this test, somehow.
t.true(true);
});

test('Throws "EINVALIDNEXTVERSION" if next release is out of range of the current maintenance branch', async (t) => {
const {cwd, repositoryUrl} = await gitRepo(true);
await gitCommits(['feat: initial commit'], {cwd});
Expand Down