mirror of
https://github.com/crazy-max/ghaction-import-gpg.git
synced 2024-11-22 13:00:56 -05:00
Allow using any user id of the key's user ids as committer identity. Fixes #156
This commit is contained in:
parent
78fb6ec0e4
commit
5170336089
5 changed files with 73 additions and 33 deletions
|
@ -230,11 +230,12 @@ The following inputs can be used as `step.with` keys
|
||||||
Following outputs are available
|
Following outputs are available
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
|---------------|--------|---------------------------------------------------------------------------------------------------------------------------------|
|
|---------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `fingerprint` | String | Fingerprint of the GPG key (recommended as [user ID](https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html)) |
|
| `fingerprint` | String | Fingerprint of the GPG key (recommended as [user ID](https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html)) |
|
||||||
| `keyid` | String | Low 64 bits of the X.509 certificate SHA-1 fingerprint |
|
| `keyid` | String | Low 64 bits of the X.509 certificate SHA-1 fingerprint |
|
||||||
| `name` | String | Name associated with the GPG key |
|
| `name` | String | Primary name associated with the GPG key |
|
||||||
| `email` | String | Email address associated with the GPG key |
|
| `email` | String | Primary email address associated with the GPG key |
|
||||||
|
| `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 |
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,16 @@ const userInfos = [
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
flag: 'r'
|
flag: 'r'
|
||||||
}),
|
}),
|
||||||
|
primaryUserId: {
|
||||||
name: 'Joe Tester',
|
name: 'Joe Tester',
|
||||||
email: 'joe@foo.bar',
|
email: 'joe@foo.bar'
|
||||||
|
},
|
||||||
|
userIds: [
|
||||||
|
{
|
||||||
|
name: 'Joe Tester',
|
||||||
|
email: 'joe@foo.bar'
|
||||||
|
}
|
||||||
|
],
|
||||||
keyID: '7D851EB72D73BDA0',
|
keyID: '7D851EB72D73BDA0',
|
||||||
fingerprint: '27571A53B86AF0C799B38BA77D851EB72D73BDA0',
|
fingerprint: '27571A53B86AF0C799B38BA77D851EB72D73BDA0',
|
||||||
keygrip: '3E2D1142AA59E08E16B7E2C64BA6DDC773B1A627'
|
keygrip: '3E2D1142AA59E08E16B7E2C64BA6DDC773B1A627'
|
||||||
|
@ -37,8 +45,16 @@ const userInfos = [
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
flag: 'r'
|
flag: 'r'
|
||||||
}),
|
}),
|
||||||
|
primaryUserId: {
|
||||||
name: 'Joe Bar',
|
name: 'Joe Bar',
|
||||||
email: 'joe@bar.foo',
|
email: 'joe@bar.foo'
|
||||||
|
},
|
||||||
|
userIds: [
|
||||||
|
{
|
||||||
|
name: 'Joe Bar',
|
||||||
|
email: 'joe@bar.foo'
|
||||||
|
}
|
||||||
|
],
|
||||||
keyID: '6071D218380FDCC8',
|
keyID: '6071D218380FDCC8',
|
||||||
fingerprint: '87F257B89CE462100BEC0FFE6071D218380FDCC8',
|
fingerprint: '87F257B89CE462100BEC0FFE6071D218380FDCC8',
|
||||||
keygrips: ['F5C3ABFAAB36B427FD98C4EDD0387E08EA1E8092', 'DEE0FC98F441519CA5DE5D79773CB29009695FEB']
|
keygrips: ['F5C3ABFAAB36B427FD98C4EDD0387E08EA1E8092', 'DEE0FC98F441519CA5DE5D79773CB29009695FEB']
|
||||||
|
@ -52,16 +68,22 @@ for (const userInfo of userInfos) {
|
||||||
it('returns a PGP private key from an armored string', async () => {
|
it('returns a PGP private key from an armored string', async () => {
|
||||||
await openpgp.readPrivateKey(userInfo.pgp).then(privateKey => {
|
await openpgp.readPrivateKey(userInfo.pgp).then(privateKey => {
|
||||||
expect(privateKey.keyID).toEqual(userInfo.keyID);
|
expect(privateKey.keyID).toEqual(userInfo.keyID);
|
||||||
expect(privateKey.name).toEqual(userInfo.name);
|
expect(privateKey.primaryUserId.name).toEqual(userInfo.primaryUserId.name);
|
||||||
expect(privateKey.email).toEqual(userInfo.email);
|
expect(privateKey.primaryUserId.email).toEqual(userInfo.primaryUserId.email);
|
||||||
|
expect(privateKey.allUserIds).toHaveLength(userInfo.userIds.length);
|
||||||
|
expect(privateKey.allUserIds[0].name).toEqual(userInfo.userIds[0].name);
|
||||||
|
expect(privateKey.allUserIds[0].email).toEqual(userInfo.userIds[0].email);
|
||||||
expect(privateKey.fingerprint).toEqual(userInfo.fingerprint);
|
expect(privateKey.fingerprint).toEqual(userInfo.fingerprint);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('returns a PGP private key from a base64 armored string', async () => {
|
it('returns a PGP private key from a base64 armored string', async () => {
|
||||||
await openpgp.readPrivateKey(userInfo.pgp_base64).then(privateKey => {
|
await openpgp.readPrivateKey(userInfo.pgp_base64).then(privateKey => {
|
||||||
expect(privateKey.keyID).toEqual(userInfo.keyID);
|
expect(privateKey.keyID).toEqual(userInfo.keyID);
|
||||||
expect(privateKey.name).toEqual(userInfo.name);
|
expect(privateKey.primaryUserId.name).toEqual(userInfo.primaryUserId.name);
|
||||||
expect(privateKey.email).toEqual(userInfo.email);
|
expect(privateKey.primaryUserId.email).toEqual(userInfo.primaryUserId.email);
|
||||||
|
expect(privateKey.allUserIds).toHaveLength(userInfo.userIds.length);
|
||||||
|
expect(privateKey.allUserIds[0].name).toEqual(userInfo.userIds[0].name);
|
||||||
|
expect(privateKey.allUserIds[0].email).toEqual(userInfo.userIds[0].email);
|
||||||
expect(privateKey.fingerprint).toEqual(userInfo.fingerprint);
|
expect(privateKey.fingerprint).toEqual(userInfo.fingerprint);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -69,7 +91,7 @@ for (const userInfo of userInfos) {
|
||||||
|
|
||||||
describe('generateKeyPair', () => {
|
describe('generateKeyPair', () => {
|
||||||
it('generates a PGP key pair', async () => {
|
it('generates a PGP key pair', async () => {
|
||||||
await openpgp.generateKeyPair(userInfo.name, userInfo.email, userInfo.passphrase).then(keyPair => {
|
await openpgp.generateKeyPair(userInfo.primaryUserId.name, userInfo.primaryUserId.email, userInfo.passphrase).then(keyPair => {
|
||||||
expect(keyPair).not.toBeUndefined();
|
expect(keyPair).not.toBeUndefined();
|
||||||
expect(keyPair.publicKey).not.toBeUndefined();
|
expect(keyPair.publicKey).not.toBeUndefined();
|
||||||
expect(keyPair.privateKey).not.toBeUndefined();
|
expect(keyPair.privateKey).not.toBeUndefined();
|
||||||
|
|
|
@ -56,9 +56,11 @@ outputs:
|
||||||
keyid:
|
keyid:
|
||||||
description: 'Low 64 bits of the X.509 certificate SHA-1 fingerprint'
|
description: 'Low 64 bits of the X.509 certificate SHA-1 fingerprint'
|
||||||
name:
|
name:
|
||||||
description: 'Name associated with the GPG key'
|
description: 'Primary name associated with the GPG key'
|
||||||
email:
|
email:
|
||||||
description: 'Email address associated with the GPG key'
|
description: 'Primary email address associated with the GPG key'
|
||||||
|
userids:
|
||||||
|
description: 'All user ids (including primary) associated with the GPG Key'
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: 'node20'
|
using: 'node20'
|
||||||
|
|
25
src/main.ts
25
src/main.ts
|
@ -28,8 +28,10 @@ async function run(): Promise<void> {
|
||||||
await core.group(`GPG private key info`, async () => {
|
await core.group(`GPG private key info`, async () => {
|
||||||
core.info(`Fingerprint : ${privateKey.fingerprint}`);
|
core.info(`Fingerprint : ${privateKey.fingerprint}`);
|
||||||
core.info(`KeyID : ${privateKey.keyID}`);
|
core.info(`KeyID : ${privateKey.keyID}`);
|
||||||
core.info(`Name : ${privateKey.name}`);
|
for (const userId of privateKey.allUserIds) {
|
||||||
core.info(`Email : ${privateKey.email}`);
|
const isPrimary = userId.email === privateKey.primaryUserId.email;
|
||||||
|
core.info(`User ID : ${userId.name} <${userId.email}>${isPrimary ? ' (primary)' : ''}`);
|
||||||
|
}
|
||||||
core.info(`CreationTime : ${privateKey.creationTime}`);
|
core.info(`CreationTime : ${privateKey.creationTime}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -91,21 +93,24 @@ async function run(): Promise<void> {
|
||||||
core.setOutput('fingerprint', fingerprint);
|
core.setOutput('fingerprint', fingerprint);
|
||||||
core.info(`keyid=${privateKey.keyID}`);
|
core.info(`keyid=${privateKey.keyID}`);
|
||||||
core.setOutput('keyid', privateKey.keyID);
|
core.setOutput('keyid', privateKey.keyID);
|
||||||
core.info(`name=${privateKey.name}`);
|
core.info(`name=${privateKey.primaryUserId.name}`);
|
||||||
core.setOutput('name', privateKey.name);
|
core.setOutput('name', privateKey.primaryUserId.name);
|
||||||
core.info(`email=${privateKey.email}`);
|
core.info(`email=${privateKey.primaryUserId.email}`);
|
||||||
core.setOutput('email', privateKey.email);
|
core.setOutput('email', privateKey.primaryUserId.email);
|
||||||
|
core.info(`userids=${JSON.stringify(privateKey.allUserIds)}`);
|
||||||
|
core.setOutput('userids', privateKey.allUserIds);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (inputs.gitUserSigningkey) {
|
if (inputs.gitUserSigningkey) {
|
||||||
core.info('Setting GPG signing keyID for this Git repository');
|
core.info('Setting GPG signing keyID for this Git repository');
|
||||||
await git.setConfig('user.signingkey', privateKey.keyID, inputs.gitConfigGlobal);
|
await git.setConfig('user.signingkey', privateKey.keyID, inputs.gitConfigGlobal);
|
||||||
|
|
||||||
const userEmail = inputs.gitCommitterEmail || privateKey.email;
|
const userName = inputs.gitCommitterName || privateKey.primaryUserId.name;
|
||||||
const userName = inputs.gitCommitterName || privateKey.name;
|
const userEmail = inputs.gitCommitterEmail || privateKey.primaryUserId.email;
|
||||||
|
|
||||||
if (userEmail != privateKey.email) {
|
if (!privateKey.allUserIds.some(id => id.email === userEmail)) {
|
||||||
core.setFailed(`Committer email "${inputs.gitCommitterEmail}" (name: "${inputs.gitCommitterName}") does not match GPG private key email "${privateKey.email}" (name: "${privateKey.name}")`);
|
const keyIdentities = privateKey.allUserIds.map(id => `"${id.email}" (name: "${id.name}")`).join(', ');
|
||||||
|
core.setFailed(`Committer email "${inputs.gitCommitterEmail}" (name: "${inputs.gitCommitterName}") does not match GPG any of the private key user id email addresses: ${keyIdentities}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import * as openpgp from 'openpgp';
|
import * as openpgp from 'openpgp';
|
||||||
import addressparser from 'addressparser';
|
import addressparser from 'addressparser';
|
||||||
|
|
||||||
|
export interface UserId {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PrivateKey {
|
export interface PrivateKey {
|
||||||
fingerprint: string;
|
fingerprint: string;
|
||||||
keyID: string;
|
keyID: string;
|
||||||
name: string;
|
primaryUserId: UserId;
|
||||||
email: string;
|
allUserIds: UserId[];
|
||||||
creationTime: Date;
|
creationTime: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,15 +24,20 @@ export const readPrivateKey = async (key: string): Promise<PrivateKey> => {
|
||||||
armoredKey: (await isArmored(key)) ? key : Buffer.from(key, 'base64').toString()
|
armoredKey: (await isArmored(key)) ? key : Buffer.from(key, 'base64').toString()
|
||||||
});
|
});
|
||||||
|
|
||||||
const address = await privateKey.getPrimaryUser().then(primaryUser => {
|
const primaryUserId: UserId = await privateKey.getPrimaryUser().then(primaryUser => {
|
||||||
return addressparser(primaryUser.user.userID?.userID)[0];
|
const address = addressparser(primaryUser.user.userID?.userID)[0];
|
||||||
|
return {name: address.name, email: address.address};
|
||||||
|
});
|
||||||
|
const allUserIds: UserId[] = privateKey.getUserIDs().map(userId => {
|
||||||
|
const address = addressparser(userId)[0];
|
||||||
|
return {name: address.name, email: address.address};
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fingerprint: privateKey.getFingerprint().toUpperCase(),
|
fingerprint: privateKey.getFingerprint().toUpperCase(),
|
||||||
keyID: privateKey.getKeyID().toHex().toUpperCase(),
|
keyID: privateKey.getKeyID().toHex().toUpperCase(),
|
||||||
name: address.name,
|
primaryUserId: primaryUserId,
|
||||||
email: address.address,
|
allUserIds: allUserIds,
|
||||||
creationTime: privateKey.getCreationTime()
|
creationTime: privateKey.getCreationTime()
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue