Skip to content

Commit

Permalink
fix(bundler): authentication using only env vars (#33339)
Browse files Browse the repository at this point in the history
Co-authored-by: Leandro <[email protected]>
Co-authored-by: Rhys Arkins <[email protected]>
  • Loading branch information
3 people authored Jan 8, 2025
1 parent 50e5344 commit 0fae106
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 254 deletions.
216 changes: 2 additions & 214 deletions lib/modules/manager/bundler/artifacts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,217 +435,6 @@ describe('modules/manager/bundler/artifacts', () => {
datasource.getPkgReleases.mockResolvedValueOnce({
releases: [{ version: '1.17.2' }, { version: '2.3.5' }],
});
bundlerHostRules.findAllAuthenticatable.mockReturnValue([
{
hostType: 'bundler',
matchHost: 'gems.private.com',
resolvedHost: 'gems.private.com',
username: 'some-user',
password: 'some-password',
},
]);
bundlerHostRules.getAuthenticationHeaderValue.mockReturnValue(
'some-user:some-password',
);
const execSnapshots = mockExecAll();
git.getRepoStatus.mockResolvedValueOnce(
partial<StatusResult>({
modified: ['Gemfile.lock'],
}),
);
fs.readLocalFile.mockResolvedValueOnce('Updated Gemfile.lock');
expect(
await updateArtifacts({
packageFileName: 'Gemfile',
updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
newPackageFileContent: 'Updated Gemfile content',
config,
}),
).toEqual([updatedGemfileLock]);
expect(execSnapshots).toMatchObject([
{ cmd: 'docker pull ghcr.io/containerbase/sidecar' },
{ cmd: 'docker ps --filter name=renovate_sidecar -aq' },
{
cmd:
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
'-v "/tmp/cache":"/tmp/cache" ' +
'-e BUNDLE_GEMS__PRIVATE__COM ' +
'-e GEM_HOME ' +
'-e CONTAINERBASE_CACHE_DIR ' +
'-w "/tmp/github/some/repo" ' +
'ghcr.io/containerbase/sidecar' +
' bash -l -c "' +
'install-tool ruby 1.2.0' +
' && ' +
'install-tool bundler 2.3.5' +
' && ' +
'ruby --version' +
' && ' +
'bundler lock --update foo bar' +
'"',
},
]);
});

it('injects bundler host configuration as command with bundler < 2', async () => {
GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock');
fs.readLocalFile.mockResolvedValueOnce('1.2.0');
// ruby
datasource.getPkgReleases.mockResolvedValueOnce({
releases: [
{ version: '1.0.0' },
{ version: '1.2.0' },
{ version: '1.3.0' },
],
});
bundlerHostRules.findAllAuthenticatable.mockReturnValue([
{
hostType: 'bundler',
matchHost: 'gems-private.com',
resolvedHost: 'gems-private.com',
username: 'some-user',
password: 'some-password',
},
]);
bundlerHostRules.getAuthenticationHeaderValue.mockReturnValue(
'some-user:some-password',
);
const execSnapshots = mockExecAll();
git.getRepoStatus.mockResolvedValueOnce(
partial<StatusResult>({
modified: ['Gemfile.lock'],
}),
);
fs.readLocalFile.mockResolvedValueOnce('Updated Gemfile.lock');
expect(
await updateArtifacts({
packageFileName: 'Gemfile',
updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
newPackageFileContent: 'Updated Gemfile content',
config: {
...config,
constraints: {
bundler: '1.2',
},
},
}),
).toEqual([updatedGemfileLock]);
expect(execSnapshots).toMatchObject([
{ cmd: 'docker pull ghcr.io/containerbase/sidecar' },
{ cmd: 'docker ps --filter name=renovate_sidecar -aq' },
{
cmd:
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
'-v "/tmp/cache":"/tmp/cache" ' +
'-e GEM_HOME ' +
'-e CONTAINERBASE_CACHE_DIR ' +
'-w "/tmp/github/some/repo" ' +
'ghcr.io/containerbase/sidecar' +
' bash -l -c "' +
'install-tool ruby 1.2.0' +
' && ' +
'install-tool bundler 1.2' +
' && ' +
'ruby --version' +
' && ' +
'bundler config --local gems-private.com some-user:some-password' +
' && ' +
'bundler lock --update foo bar' +
'"',
},
]);
});

it('injects bundler host configuration as command with bundler >= 2', async () => {
GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock');
fs.readLocalFile.mockResolvedValueOnce('1.2.0');
// ruby
datasource.getPkgReleases.mockResolvedValueOnce({
releases: [
{ version: '1.0.0' },
{ version: '1.2.0' },
{ version: '1.3.0' },
],
});
bundlerHostRules.findAllAuthenticatable.mockReturnValue([
{
hostType: 'bundler',
matchHost: 'gems-private.com',
resolvedHost: 'gems-private.com',
username: 'some-user',
password: 'some-password',
},
]);
bundlerHostRules.getAuthenticationHeaderValue.mockReturnValue(
'some-user:some-password',
);
const execSnapshots = mockExecAll();
git.getRepoStatus.mockResolvedValueOnce(
partial<StatusResult>({
modified: ['Gemfile.lock'],
}),
);
fs.readLocalFile.mockResolvedValueOnce('Updated Gemfile.lock');
expect(
await updateArtifacts({
packageFileName: 'Gemfile',
updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
newPackageFileContent: 'Updated Gemfile content',
config: {
...config,
constraints: {
bundler: '2.1',
},
},
}),
).toEqual([updatedGemfileLock]);
expect(execSnapshots).toMatchObject([
{ cmd: 'docker pull ghcr.io/containerbase/sidecar' },
{ cmd: 'docker ps --filter name=renovate_sidecar -aq' },
{
cmd:
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
'-v "/tmp/cache":"/tmp/cache" ' +
'-e GEM_HOME ' +
'-e CONTAINERBASE_CACHE_DIR ' +
'-w "/tmp/github/some/repo" ' +
'ghcr.io/containerbase/sidecar' +
' bash -l -c "' +
'install-tool ruby 1.2.0' +
' && ' +
'install-tool bundler 2.1' +
' && ' +
'ruby --version' +
' && ' +
'bundler config set --local gems-private.com some-user:some-password' +
' && ' +
'bundler lock --update foo bar' +
'"',
},
]);
});

it('injects bundler host configuration as command with bundler == latest', async () => {
GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock');
fs.readLocalFile.mockResolvedValueOnce('1.2.0');
// ruby
datasource.getPkgReleases.mockResolvedValueOnce({
releases: [
{ version: '1.0.0' },
{ version: '1.2.0' },
{ version: '1.3.0' },
],
});
// bundler
datasource.getPkgReleases.mockResolvedValueOnce({
releases: [{ version: '1.17.2' }, { version: '2.3.5' }],
});
bundlerHostRules.findAllAuthenticatable.mockReturnValue([
{
hostType: 'bundler',
Expand Down Expand Up @@ -681,19 +470,18 @@ describe('modules/manager/bundler/artifacts', () => {
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
'-v "/tmp/cache":"/tmp/cache" ' +
'-e BUNDLE_GEMS___PRIVATE__COM ' +
'-e GEM_HOME ' +
'-e CONTAINERBASE_CACHE_DIR ' +
'-w "/tmp/github/some/repo" ' +
'ghcr.io/containerbase/sidecar' +
' bash -l -c "' +
'install-tool ruby 1.2.0' +
' && ' +
'install-tool bundler 1.3.0' +
'install-tool bundler 2.3.5' +
' && ' +
'ruby --version' +
' && ' +
'bundler config set --local gems-private.com some-user:some-password' +
' && ' +
'bundler lock --update foo bar' +
'"',
},
Expand Down
46 changes: 6 additions & 40 deletions lib/modules/manager/bundler/artifacts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { lt } from '@renovatebot/ruby-semver';
import is from '@sindresorhus/is';
import { quote } from 'shlex';
import {
Expand All @@ -17,7 +16,6 @@ import {
} from '../../../util/fs';
import { getRepoStatus } from '../../../util/git';
import { newlineRegex, regEx } from '../../../util/regex';
import { isValid } from '../../versioning/ruby';
import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
import {
getBundlerConstraint,
Expand All @@ -32,14 +30,17 @@ import {
const hostConfigVariablePrefix = 'BUNDLE_';

function buildBundleHostVariable(hostRule: HostRule): Record<string, string> {
if (!hostRule.resolvedHost || hostRule.resolvedHost.includes('-')) {
// istanbul ignore if: doesn't happen in practice
if (!hostRule.resolvedHost) {
return {};
}
const varName = hostConfigVariablePrefix.concat(
hostRule.resolvedHost
.toUpperCase()
.split('.')
.map((term) => term.toUpperCase())
.join('__'),
.join('__')
.split('-')
.join('___'),
);
return {
[varName]: `${getAuthenticationHeaderValue(hostRule)}`,
Expand Down Expand Up @@ -149,47 +150,12 @@ export async function updateArtifacts(
{} as Record<string, string>,
);

// Detect hosts with a hyphen '-' in the url.
// Those cannot be added with environment variables but need to be added
// with the bundler config
const bundlerHostRulesAuthCommands: string[] = bundlerHostRules.reduce(
(authCommands: string[], hostRule) => {
if (hostRule.resolvedHost?.includes('-')) {
// TODO: fix me, hostrules can missing all auth
const creds = getAuthenticationHeaderValue(hostRule);
authCommands.push(`${quote(hostRule.resolvedHost)} ${quote(creds)}`);
}
return authCommands;
},
[],
);

const bundler = getBundlerConstraint(
updateArtifact,
existingLockFileContent,
);
const preCommands = ['ruby --version'];

// Bundler < 2 has a different config option syntax than >= 2
if (
bundlerHostRulesAuthCommands &&
bundler &&
isValid(bundler) &&
lt(bundler, '2')
) {
preCommands.push(
...bundlerHostRulesAuthCommands.map(
(authCommand) => `bundler config --local ${authCommand}`,
),
);
} else if (bundlerHostRulesAuthCommands) {
preCommands.push(
...bundlerHostRulesAuthCommands.map(
(authCommand) => `bundler config set --local ${authCommand}`,
),
);
}

const execOptions: ExecOptions = {
cwdFile: lockFileName,
userConfiguredEnv: config.env,
Expand Down

0 comments on commit 0fae106

Please sign in to comment.