Skip to content

Commit 5170336

Browse files
committed
Allow using any user id of the key's user ids as committer identity. Fixes #156
1 parent 78fb6ec commit 5170336

File tree

5 files changed

+73
-33
lines changed

5 files changed

+73
-33
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,13 @@ The following inputs can be used as `step.with` keys
229229

230230
Following outputs are available
231231

232-
| Name | Type | Description |
233-
|---------------|--------|---------------------------------------------------------------------------------------------------------------------------------|
234-
| `fingerprint` | String | Fingerprint of the GPG key (recommended as [user ID](https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html)) |
235-
| `keyid` | String | Low 64 bits of the X.509 certificate SHA-1 fingerprint |
236-
| `name` | String | Name associated with the GPG key |
237-
| `email` | String | Email address associated with the GPG key |
232+
| Name | Type | Description |
233+
|---------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
234+
| `fingerprint` | String | Fingerprint of the GPG key (recommended as [user ID](https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html)) |
235+
| `keyid` | String | Low 64 bits of the X.509 certificate SHA-1 fingerprint |
236+
| `name` | String | Primary name associated with the GPG key |
237+
| `email` | String | Primary email address associated with the GPG key |
238+
| `userids` | String (JSON) | All user ids (including primary) associated with the GPG Key.<br/>The output is a JSON array where each object has a `name` and `email` key. Use [fromJson](https://docs.github.com/en/actions/learn-github-actions/expressions#fromjson) to turn the String back into a JSON array |
238239

239240
## Contributing
240241

__tests__/openpgp.test.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,16 @@ const userInfos = [
1717
encoding: 'utf8',
1818
flag: 'r'
1919
}),
20-
name: 'Joe Tester',
21-
email: 'joe@foo.bar',
20+
primaryUserId: {
21+
name: 'Joe Tester',
22+
email: 'joe@foo.bar'
23+
},
24+
userIds: [
25+
{
26+
name: 'Joe Tester',
27+
email: 'joe@foo.bar'
28+
}
29+
],
2230
keyID: '7D851EB72D73BDA0',
2331
fingerprint: '27571A53B86AF0C799B38BA77D851EB72D73BDA0',
2432
keygrip: '3E2D1142AA59E08E16B7E2C64BA6DDC773B1A627'
@@ -37,8 +45,16 @@ const userInfos = [
3745
encoding: 'utf8',
3846
flag: 'r'
3947
}),
40-
name: 'Joe Bar',
41-
email: 'joe@bar.foo',
48+
primaryUserId: {
49+
name: 'Joe Bar',
50+
email: 'joe@bar.foo'
51+
},
52+
userIds: [
53+
{
54+
name: 'Joe Bar',
55+
email: 'joe@bar.foo'
56+
}
57+
],
4258
keyID: '6071D218380FDCC8',
4359
fingerprint: '87F257B89CE462100BEC0FFE6071D218380FDCC8',
4460
keygrips: ['F5C3ABFAAB36B427FD98C4EDD0387E08EA1E8092', 'DEE0FC98F441519CA5DE5D79773CB29009695FEB']
@@ -52,24 +68,30 @@ for (const userInfo of userInfos) {
5268
it('returns a PGP private key from an armored string', async () => {
5369
await openpgp.readPrivateKey(userInfo.pgp).then(privateKey => {
5470
expect(privateKey.keyID).toEqual(userInfo.keyID);
55-
expect(privateKey.name).toEqual(userInfo.name);
56-
expect(privateKey.email).toEqual(userInfo.email);
71+
expect(privateKey.primaryUserId.name).toEqual(userInfo.primaryUserId.name);
72+
expect(privateKey.primaryUserId.email).toEqual(userInfo.primaryUserId.email);
73+
expect(privateKey.allUserIds).toHaveLength(userInfo.userIds.length);
74+
expect(privateKey.allUserIds[0].name).toEqual(userInfo.userIds[0].name);
75+
expect(privateKey.allUserIds[0].email).toEqual(userInfo.userIds[0].email);
5776
expect(privateKey.fingerprint).toEqual(userInfo.fingerprint);
5877
});
5978
});
6079
it('returns a PGP private key from a base64 armored string', async () => {
6180
await openpgp.readPrivateKey(userInfo.pgp_base64).then(privateKey => {
6281
expect(privateKey.keyID).toEqual(userInfo.keyID);
63-
expect(privateKey.name).toEqual(userInfo.name);
64-
expect(privateKey.email).toEqual(userInfo.email);
82+
expect(privateKey.primaryUserId.name).toEqual(userInfo.primaryUserId.name);
83+
expect(privateKey.primaryUserId.email).toEqual(userInfo.primaryUserId.email);
84+
expect(privateKey.allUserIds).toHaveLength(userInfo.userIds.length);
85+
expect(privateKey.allUserIds[0].name).toEqual(userInfo.userIds[0].name);
86+
expect(privateKey.allUserIds[0].email).toEqual(userInfo.userIds[0].email);
6587
expect(privateKey.fingerprint).toEqual(userInfo.fingerprint);
6688
});
6789
});
6890
});
6991

7092
describe('generateKeyPair', () => {
7193
it('generates a PGP key pair', async () => {
72-
await openpgp.generateKeyPair(userInfo.name, userInfo.email, userInfo.passphrase).then(keyPair => {
94+
await openpgp.generateKeyPair(userInfo.primaryUserId.name, userInfo.primaryUserId.email, userInfo.passphrase).then(keyPair => {
7395
expect(keyPair).not.toBeUndefined();
7496
expect(keyPair.publicKey).not.toBeUndefined();
7597
expect(keyPair.privateKey).not.toBeUndefined();

action.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ outputs:
5656
keyid:
5757
description: 'Low 64 bits of the X.509 certificate SHA-1 fingerprint'
5858
name:
59-
description: 'Name associated with the GPG key'
59+
description: 'Primary name associated with the GPG key'
6060
email:
61-
description: 'Email address associated with the GPG key'
61+
description: 'Primary email address associated with the GPG key'
62+
userids:
63+
description: 'All user ids (including primary) associated with the GPG Key'
6264

6365
runs:
6466
using: 'node20'

src/main.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ async function run(): Promise<void> {
2828
await core.group(`GPG private key info`, async () => {
2929
core.info(`Fingerprint : ${privateKey.fingerprint}`);
3030
core.info(`KeyID : ${privateKey.keyID}`);
31-
core.info(`Name : ${privateKey.name}`);
32-
core.info(`Email : ${privateKey.email}`);
31+
for (const userId of privateKey.allUserIds) {
32+
const isPrimary = userId.email === privateKey.primaryUserId.email;
33+
core.info(`User ID : ${userId.name} <${userId.email}>${isPrimary ? ' (primary)' : ''}`);
34+
}
3335
core.info(`CreationTime : ${privateKey.creationTime}`);
3436
});
3537

@@ -91,21 +93,24 @@ async function run(): Promise<void> {
9193
core.setOutput('fingerprint', fingerprint);
9294
core.info(`keyid=${privateKey.keyID}`);
9395
core.setOutput('keyid', privateKey.keyID);
94-
core.info(`name=${privateKey.name}`);
95-
core.setOutput('name', privateKey.name);
96-
core.info(`email=${privateKey.email}`);
97-
core.setOutput('email', privateKey.email);
96+
core.info(`name=${privateKey.primaryUserId.name}`);
97+
core.setOutput('name', privateKey.primaryUserId.name);
98+
core.info(`email=${privateKey.primaryUserId.email}`);
99+
core.setOutput('email', privateKey.primaryUserId.email);
100+
core.info(`userids=${JSON.stringify(privateKey.allUserIds)}`);
101+
core.setOutput('userids', privateKey.allUserIds);
98102
});
99103

100104
if (inputs.gitUserSigningkey) {
101105
core.info('Setting GPG signing keyID for this Git repository');
102106
await git.setConfig('user.signingkey', privateKey.keyID, inputs.gitConfigGlobal);
103107

104-
const userEmail = inputs.gitCommitterEmail || privateKey.email;
105-
const userName = inputs.gitCommitterName || privateKey.name;
108+
const userName = inputs.gitCommitterName || privateKey.primaryUserId.name;
109+
const userEmail = inputs.gitCommitterEmail || privateKey.primaryUserId.email;
106110

107-
if (userEmail != privateKey.email) {
108-
core.setFailed(`Committer email "${inputs.gitCommitterEmail}" (name: "${inputs.gitCommitterName}") does not match GPG private key email "${privateKey.email}" (name: "${privateKey.name}")`);
111+
if (!privateKey.allUserIds.some(id => id.email === userEmail)) {
112+
const keyIdentities = privateKey.allUserIds.map(id => `"${id.email}" (name: "${id.name}")`).join(', ');
113+
core.setFailed(`Committer email "${inputs.gitCommitterEmail}" (name: "${inputs.gitCommitterName}") does not match GPG any of the private key user id email addresses: ${keyIdentities}`);
109114
return;
110115
}
111116

src/openpgp.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import * as openpgp from 'openpgp';
22
import addressparser from 'addressparser';
33

4+
export interface UserId {
5+
name: string;
6+
email: string;
7+
}
8+
49
export interface PrivateKey {
510
fingerprint: string;
611
keyID: string;
7-
name: string;
8-
email: string;
12+
primaryUserId: UserId;
13+
allUserIds: UserId[];
914
creationTime: Date;
1015
}
1116

@@ -19,15 +24,20 @@ export const readPrivateKey = async (key: string): Promise<PrivateKey> => {
1924
armoredKey: (await isArmored(key)) ? key : Buffer.from(key, 'base64').toString()
2025
});
2126

22-
const address = await privateKey.getPrimaryUser().then(primaryUser => {
23-
return addressparser(primaryUser.user.userID?.userID)[0];
27+
const primaryUserId: UserId = await privateKey.getPrimaryUser().then(primaryUser => {
28+
const address = addressparser(primaryUser.user.userID?.userID)[0];
29+
return {name: address.name, email: address.address};
30+
});
31+
const allUserIds: UserId[] = privateKey.getUserIDs().map(userId => {
32+
const address = addressparser(userId)[0];
33+
return {name: address.name, email: address.address};
2434
});
2535

2636
return {
2737
fingerprint: privateKey.getFingerprint().toUpperCase(),
2838
keyID: privateKey.getKeyID().toHex().toUpperCase(),
29-
name: address.name,
30-
email: address.address,
39+
primaryUserId: primaryUserId,
40+
allUserIds: allUserIds,
3141
creationTime: privateKey.getCreationTime()
3242
};
3343
};

0 commit comments

Comments
 (0)