-
Notifications
You must be signed in to change notification settings - Fork 18
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
Add GPG Signature option #337
Comments
Is there a reason this PR is still open, isn't this now supported? @chingor13 |
Hi @IchordeDionysos, Could you tell how to configure release-please to sign commits? |
@Zebradil I'm testing around myself! My current status:
I'm attaching my WIP code for signing the commit: WIP commit signing
// These would be constructed/passed by release-please.
const octokit = new Octokit();
const fileData = new FileData('hello world', '100644');
const changes = new Map();
changes.set('foo.md', fileData);
// The PR would be created by release-please according to the release-please configuration.
await suggester.createPullRequest(octokit, changes, {
upstreamOwner: '<owner>',
upstreamRepo: '<repo>',
title: 'Test PR',
message: 'Test commit',
description: '',
fork: false,
force: true,
author: {
name: '<authorName>',
email: '<authorEmail>,
},
committer: {
name: '<committerName>',
email: '<committerEmail>',
},
// The next part would have to be added to release-please
signer: {
async generateSignature(commit: CommitData) {
// We'd need to figure out how to pass the private key and an optional passphrase for signing.
const privateKey = await openpgp.readPrivateKey({
armoredKey: privateKeyArmored,
});
// Here the commit/authored date is missing, otherwise, the payload looks good.
const commitObject = `tree ${commit.tree}
${commit.parents.map(parent => `parent ${parent}`).join('\n')}
author ${commit.author?.name ?? ''} <${commit.author?.email ?? ''}>
committer ${commit.committer?.name ?? ''} <${commit.committer?.email ?? ''}>
${commit.message}`;
console.log(commit);
console.log(commitObject);
const message = await openpgp.createCleartextMessage({
text: commitObject,
});
const signedMessage = await openpgp.sign({
message,
signingKeys: privateKey,
});
// We maybe need to find a more elegant solution to get only the signature (the `signedMessage` contains the passed message and only at the end the signature is attached)
const signature =
'-----BEGIN PGP SIGNATURE-----' +
signedMessage.split('-----BEGIN PGP SIGNATURE-----')[1];
console.log('signature', signature);
return signature;
},
},
}); Next steps to support code signing properly:
|
Okay, a quick update from my side. The commit signing was done using this signer: class GPGCommitSigner implements CommitSigner {
private privateKey: string;
private passphrase: string | undefined;
constructor({
privateKey,
passphrase,
}: {
privateKey: string;
passphrase?: string;
}) {
this.privateKey = privateKey;
this.passphrase = passphrase;
}
async generateSignature(commit: CommitDataWithRequiredDate): Promise<string> {
const privateKey = await openpgp.readPrivateKey({
armoredKey: this.privateKey,
});
const commitObject = this.buildCommitObject(commit);
const message = await openpgp.createCleartextMessage({
text: commitObject,
});
const signedMessage = await openpgp.sign({
message,
signingKeys: privateKey,
// todo: Figure out how to use passphrases
});
return this.parseSignature(signedMessage);
}
private buildCommitObject(commit: CommitDataWithRequiredDate) {
const rows = [];
rows.push(`tree ${commit.tree}`);
for (const parent of commit.parents) {
rows.push(`parent ${parent}`);
}
if (commit.author) {
rows.push(`author ${this.buildUserCommitObjectRow(commit.author)}`);
}
if (commit.committer) {
rows.push(`committer ${this.buildUserCommitObjectRow(commit.committer)}`);
}
rows.push('');
rows.push(commit.message);
return rows.join('\n');
}
private buildUserCommitObjectRow({
name,
email,
date,
}: {
name: string;
email: string;
date: Date;
}) {
const unixDate = Math.floor(date.getTime() / 1000);
const timezoneOffset = this.buildTimezoneOffset(date);
return `${name} <${email}> ${unixDate} ${timezoneOffset}`;
}
private buildTimezoneOffset(date: Date): string {
const timezoneOffset = date.getTimezoneOffset();
const timezoneOffsetAbsolute = Math.abs(timezoneOffset);
if (timezoneOffset === 0) {
return '+0000';
}
const offsetInHourFormat = (timezoneOffsetAbsolute / 60) * 100;
const offsetString = String(offsetInHourFormat).padStart(4, '0');
if (timezoneOffset < 0) {
return `+${offsetString}`;
}
return `-${offsetString}`;
}
private parseSignature(signedMessage: string): string {
const signature =
'-----BEGIN PGP SIGNATURE-----' +
signedMessage.split('-----BEGIN PGP SIGNATURE-----')[1];
return signature;
}
} And the generated commit is verified as you can see here: simpleclub-extended@ca1c82b I called the function like this: const octokit = new Octokit();
const fileData = new FileData('hello world', '100644');
const changes = new Map();
changes.set('foo.md', fileData);
await suggester.createPullRequest(octokit, changes, {
upstreamOwner: 'simpleclub-extended',
upstreamRepo: 'code-suggester',
title: 'Test commit signing using GPG keys',
message: 'Test signed commit',
description: '',
fork: false,
force: true,
author: {
name: '<name>',
email: '<email>',
},
committer: {
name: '<name>',
email: '<email>',
},
signer: new GPGCommitSigner({privateKey: privateKeyArmored}),
}); @chingor13 could you review my PR to expose the date and let me know if you have a preference for where to best put the |
The GitHub commit API allows signing a commit, but the signature must be calculated externally.
https://docs.github.com/en/rest/reference/git#create-a-commit
The text was updated successfully, but these errors were encountered: