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(managers/git-submodules): support updating git-tag versions #30104

Merged
merged 6 commits into from
Jul 26, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/modules/manager/git-submodules/__fixtures__/.gitmodules.8
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[submodule "renovate1"]
path = deps/renovate1
url = https://github.com/renovatebot/renovate.git
branch = v0.0.1
[submodule "renovate2"]
path = deps/renovate2
url = https://github.com/renovatebot/renovate.git
branch = 0.0.1
[submodule "renovate3"]
path = deps/renovate3
url = https://github.com/renovatebot/renovate.git
branch = not-a-semver
44 changes: 40 additions & 4 deletions lib/modules/manager/git-submodules/artifact.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { fs } from '../../../../test/util';
import { updateArtifacts } from '.';

jest.mock('../../../util/fs');

describe('modules/manager/git-submodules/artifact', () => {
describe('updateArtifacts()', () => {
it('returns empty content', () => {
it('returns empty content', async () => {
expect(
updateArtifacts({
await updateArtifacts({
packageFileName: '',
updatedDeps: [{ depName: '' }],
newPackageFileContent: '',
Expand All @@ -13,9 +16,9 @@ describe('modules/manager/git-submodules/artifact', () => {
).toMatchObject([{ file: { type: 'addition', path: '', contents: '' } }]);
});

it('returns two modules', () => {
it('returns two modules', async () => {
expect(
updateArtifacts({
await updateArtifacts({
packageFileName: '',
updatedDeps: [{ depName: 'renovate' }, { depName: 'renovate-pro' }],
newPackageFileContent: '',
Expand All @@ -26,5 +29,38 @@ describe('modules/manager/git-submodules/artifact', () => {
{ file: { type: 'addition', path: 'renovate-pro', contents: '' } },
]);
});

it('returns updated .gitmodules with new value as branch value', async () => {
const updatedGitModules = `[submodule "renovate"]
path = deps/renovate
url = https://github.com/renovatebot/renovate.git
branch = v0.0.2`;
fs.readLocalFile.mockResolvedValueOnce(updatedGitModules);
expect(
await updateArtifacts({
packageFileName: '',
updatedDeps: [
{
depName: 'renovate',
currentValue: 'v0.0.1',
newValue: 'v0.0.2',
packageFile: '.gitmodules',
},
],
newPackageFileContent: '',
config: {},
}),
).toMatchObject([
{ file: { type: 'addition', path: 'renovate', contents: '' } },
{
file: {
type: 'addition',
path: '.gitmodules',
contents: updatedGitModules,
},
},
]);
expect(fs.readLocalFile).toHaveBeenCalledWith('.gitmodules');
});
});
});
18 changes: 14 additions & 4 deletions lib/modules/manager/git-submodules/artifacts.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { logger } from '../../../logger';
import { readLocalFile } from '../../../util/fs';
import type { UpdateArtifact, UpdateArtifactsResult } from '../types';

export default function updateArtifacts({
export default async function updateArtifacts({
updatedDeps,
}: UpdateArtifact): UpdateArtifactsResult[] | null {
}: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> {
const res: UpdateArtifactsResult[] = [];
updatedDeps.forEach((dep) => {
for (const dep of updatedDeps) {
Shegox marked this conversation as resolved.
Show resolved Hide resolved
// TODO: types (#22198)
logger.info(`Updating submodule ${dep.depName}`);
res.push({
file: { type: 'addition', path: dep.depName!, contents: '' },
});
});
if (dep.newValue && dep.currentValue !== dep.newValue) {
res.push({
file: {
type: 'addition',
path: dep.packageFile!,
contents: await readLocalFile(dep.packageFile!),
},
});
}
}
Shegox marked this conversation as resolved.
Show resolved Hide resolved
return res;
}
30 changes: 30 additions & 0 deletions lib/modules/manager/git-submodules/extract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ describe('modules/manager/git-submodules/extract', () => {
it('default to master if no branch can be detected', async () => {
const res = await extractPackageFile('', '.gitmodules.2', {});
expect(res?.deps).toHaveLength(1);
expect(res?.deps[0].versioning).toBeUndefined();
expect(res?.deps[0].currentValue).toBe('master');
});

Expand Down Expand Up @@ -336,5 +337,34 @@ describe('modules/manager/git-submodules/extract', () => {
],
});
});

it('given semver version is extracted from branch and versioning is set to semver-coerced', async () => {
const res = await extractPackageFile('', '.gitmodules.8', {});
expect(res).toEqual({
datasource: 'git-refs',
deps: [
{
currentDigest: '4b825dc642cb6eb9a060e54bf8d69288fbee4904',
currentValue: 'v0.0.1',
depName: 'deps/renovate1',
packageName: 'https://github.com/renovatebot/renovate.git',
versioning: 'semver-coerced',
},
{
currentDigest: '4b825dc642cb6eb9a060e54bf8d69288fbee4904',
currentValue: '0.0.1',
depName: 'deps/renovate2',
packageName: 'https://github.com/renovatebot/renovate.git',
versioning: 'semver-coerced',
},
{
currentDigest: '4b825dc642cb6eb9a060e54bf8d69288fbee4904',
currentValue: 'not-a-semver',
packageName: 'https://github.com/renovatebot/renovate.git',
depName: 'deps/renovate3',
},
],
});
});
});
});
4 changes: 4 additions & 0 deletions lib/modules/manager/git-submodules/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { simpleGitConfig } from '../../../util/git/config';
import { getHttpUrl } from '../../../util/git/url';
import { regEx } from '../../../util/regex';
import { GitRefsDatasource } from '../../datasource/git-refs';
import * as semVerCoercedVersioning from '../../versioning/semver-coerced';
import type { ExtractConfig, PackageFileContent } from '../types';
import type { GitModule } from './types';

Expand Down Expand Up @@ -138,6 +139,9 @@ export default async function extractPackageFile(
packageName: httpSubModuleUrl,
currentValue,
currentDigest,
...(semVerCoercedVersioning.api.isVersion(currentValue)
Shegox marked this conversation as resolved.
Show resolved Hide resolved
? { versioning: semVerCoercedVersioning.id }
: {}),
});
} catch (err) /* istanbul ignore next */ {
logger.warn(
Expand Down
13 changes: 13 additions & 0 deletions lib/modules/manager/git-submodules/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ You can customize the per-submodule checks of the git-submodules manager like th
}
```

### Updating to tag values

If you want to update your git submodules to a specific tag, you can set the current tag as `branch` in the `.gitmodules`-files and Renovate will update this version to the latest git tag.

```ini
[submodule "renovate"]
path = deps/renovate
url = https://github.com/renovatebot/renovate.git
branch = v0.0.1
```

Notice: Using this will break the native git submodule update experience using `git submodule update --remote` with an error like `fatal: Unable to find refs/remotes ... revision in submodule path ...`.
Shegox marked this conversation as resolved.
Show resolved Hide resolved

### Private Modules Authentication

Before running the `git` commands to update the submodules, Renovate exports `git` [`insteadOf`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-urlltbasegtinsteadOf) directives in environment variables.
Expand Down
39 changes: 39 additions & 0 deletions lib/modules/manager/git-submodules/update.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,45 @@ describe('modules/manager/git-submodules/update', () => {
});
});

it('update gitmodule branch value if value changed', async () => {
gitMock.submoduleUpdate.mockResolvedValue('');
gitMock.checkout.mockResolvedValue('');
upgrade = {
depName: 'renovate',
currentValue: 'v0.0.1',
newValue: 'v0.0.2',
packageFile: '.gitmodules',
};
const update = await updateDependency({
fileContent: '',
upgrade,
});
expect(update).toBe('');
expect(gitMock.subModule).toHaveBeenCalledWith([
'set-branch',
'--branch',
'v0.0.2',
'renovate',
]);
});

it('do not update gitmodule branch value if value not changed', async () => {
gitMock.submoduleUpdate.mockResolvedValue('');
gitMock.checkout.mockResolvedValue('');
upgrade = {
depName: 'renovate',
currentValue: 'main',
newValue: 'main',
packageFile: '.gitmodules',
};
const update = await updateDependency({
fileContent: '',
upgrade,
});
expect(update).toBe('');
expect(gitMock.subModule).toHaveBeenCalledTimes(0);
});

it('returns content on update and uses git environment variables for git-tags/git-refs', async () => {
gitMock.submoduleUpdate.mockResolvedValue('');
gitMock.checkout.mockResolvedValue('');
Expand Down
8 changes: 8 additions & 0 deletions lib/modules/manager/git-submodules/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ export default async function updateDependency({
try {
await git.submoduleUpdate(['--init', upgrade.depName!]);
await submoduleGit.checkout([upgrade.newDigest!]);
if (upgrade.newValue && upgrade.currentValue !== upgrade.newValue) {
await git.subModule([
'set-branch',
'--branch',
upgrade.newValue,
upgrade.depName!,
]);
}
return fileContent;
Shegox marked this conversation as resolved.
Show resolved Hide resolved
} catch (err) {
logger.debug({ err }, 'submodule checkout error');
Expand Down