mirror of
https://github.com/crazy-max/ghaction-import-gpg.git
synced 2024-12-25 20:52:06 -05:00
input to set private key trust level
This commit is contained in:
parent
ff084959ff
commit
05fea506c1
7 changed files with 158 additions and 26 deletions
63
.github/workflows/ci.yml
vendored
63
.github/workflows/ci.yml
vendored
|
@ -55,6 +55,7 @@ jobs:
|
||||||
if (!fs.existsSync(gnupgfolder)){
|
if (!fs.existsSync(gnupgfolder)){
|
||||||
fs.mkdirSync(gnupgfolder);
|
fs.mkdirSync(gnupgfolder);
|
||||||
}
|
}
|
||||||
|
fs.chmodSync(gnupgfolder, '0700');
|
||||||
fs.copyFile('__tests__/fixtures/gpg.conf', `${gnupgfolder}/gpg.conf`, (err) => {
|
fs.copyFile('__tests__/fixtures/gpg.conf', `${gnupgfolder}/gpg.conf`, (err) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
});
|
});
|
||||||
|
@ -69,11 +70,11 @@ jobs:
|
||||||
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
|
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
|
||||||
-
|
-
|
||||||
name: Import GPG
|
name: Import GPG
|
||||||
id: import_gpg
|
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ steps.test.outputs.pgp }}
|
gpg_private_key: ${{ steps.test.outputs.pgp }}
|
||||||
passphrase: ${{ steps.test.outputs.passphrase }}
|
passphrase: ${{ steps.test.outputs.passphrase }}
|
||||||
|
trust_level: 5
|
||||||
git_config_global: ${{ matrix.global }}
|
git_config_global: ${{ matrix.global }}
|
||||||
git_user_signingkey: true
|
git_user_signingkey: true
|
||||||
git_commit_gpgsign: true
|
git_commit_gpgsign: true
|
||||||
|
@ -116,7 +117,6 @@ jobs:
|
||||||
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
|
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
|
||||||
-
|
-
|
||||||
name: Import GPG
|
name: Import GPG
|
||||||
id: import_gpg
|
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ steps.test.outputs.pgp-base64 }}
|
gpg_private_key: ${{ steps.test.outputs.pgp-base64 }}
|
||||||
|
@ -126,3 +126,62 @@ jobs:
|
||||||
git_tag_gpgsign: true
|
git_tag_gpgsign: true
|
||||||
git_push_gpgsign: if-asked
|
git_push_gpgsign: if-asked
|
||||||
fingerprint: ${{ matrix.fingerprint }}
|
fingerprint: ${{ matrix.fingerprint }}
|
||||||
|
|
||||||
|
trust:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
key:
|
||||||
|
- test-key
|
||||||
|
level:
|
||||||
|
- ''
|
||||||
|
- 5
|
||||||
|
- 4
|
||||||
|
- 3
|
||||||
|
- 2
|
||||||
|
- 1
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
- macOS-latest
|
||||||
|
- windows-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: GPG conf
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const gnupgfolder = `${require('os').homedir()}/.gnupg`;
|
||||||
|
if (!fs.existsSync(gnupgfolder)){
|
||||||
|
fs.mkdirSync(gnupgfolder);
|
||||||
|
}
|
||||||
|
fs.chmodSync(gnupgfolder, '0700');
|
||||||
|
fs.copyFile('__tests__/fixtures/gpg.conf', `${gnupgfolder}/gpg.conf`, (err) => {
|
||||||
|
if (err) throw err;
|
||||||
|
});
|
||||||
|
-
|
||||||
|
name: Get test key and passphrase
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
id: test
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
core.setOutput('pgp', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pgp', {encoding: 'utf8'}));
|
||||||
|
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
|
||||||
|
-
|
||||||
|
name: Import GPG
|
||||||
|
id: import_gpg
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
gpg_private_key: ${{ steps.test.outputs.pgp }}
|
||||||
|
passphrase: ${{ steps.test.outputs.passphrase }}
|
||||||
|
trust_level: ${{ matrix.level }}
|
||||||
|
-
|
||||||
|
name: List trust values
|
||||||
|
run: |
|
||||||
|
gpg --export-ownertrust
|
||||||
|
shell: bash
|
||||||
|
|
78
README.md
78
README.md
|
@ -19,6 +19,7 @@ ___
|
||||||
* [Workflow](#workflow)
|
* [Workflow](#workflow)
|
||||||
* [Sign commits](#sign-commits)
|
* [Sign commits](#sign-commits)
|
||||||
* [Use a subkey](#use-a-subkey)
|
* [Use a subkey](#use-a-subkey)
|
||||||
|
* [Set key's trust level](#set-keys-trust-level)
|
||||||
* [Customizing](#customizing)
|
* [Customizing](#customizing)
|
||||||
* [inputs](#inputs)
|
* [inputs](#inputs)
|
||||||
* [outputs](#outputs)
|
* [outputs](#outputs)
|
||||||
|
@ -76,7 +77,6 @@ jobs:
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
-
|
-
|
||||||
name: Import GPG key
|
name: Import GPG key
|
||||||
id: import_gpg
|
|
||||||
uses: crazy-max/ghaction-import-gpg@v5
|
uses: crazy-max/ghaction-import-gpg@v5
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
@ -139,7 +139,6 @@ jobs:
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
-
|
-
|
||||||
name: Import GPG key
|
name: Import GPG key
|
||||||
id: import_gpg
|
|
||||||
uses: crazy-max/ghaction-import-gpg@v5
|
uses: crazy-max/ghaction-import-gpg@v5
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
@ -164,26 +163,63 @@ sub ed25519 2021-09-24 [S]
|
||||||
|
|
||||||
You can use the subkey with signing capability whose fingerprint is `C17D11ADF199F12A30A0910F1F80449BE0B08CB8`.
|
You can use the subkey with signing capability whose fingerprint is `C17D11ADF199F12A30A0910F1F80449BE0B08CB8`.
|
||||||
|
|
||||||
|
### Set key's trust level
|
||||||
|
|
||||||
|
With the `trust_level` input, you can specify the trust level of the GPG key.
|
||||||
|
|
||||||
|
Valid values are:
|
||||||
|
* `1`: unknown
|
||||||
|
* `2`: never
|
||||||
|
* `3`: marginal
|
||||||
|
* `4`: full
|
||||||
|
* `5`: ultimate
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: import-gpg
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
import-gpg:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: Import GPG key
|
||||||
|
uses: crazy-max/ghaction-import-gpg@v5
|
||||||
|
with:
|
||||||
|
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
passphrase: ${{ secrets.PASSPHRASE }}
|
||||||
|
trust_level: 5
|
||||||
|
```
|
||||||
|
|
||||||
## Customizing
|
## Customizing
|
||||||
|
|
||||||
### inputs
|
### inputs
|
||||||
|
|
||||||
Following inputs can be used as `step.with` keys
|
Following inputs can be used as `step.with` keys
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
|---------------------------------------|---------|------------------------------------------------|
|
|-----------------------|--------|--------------------------------------------------------------------------------------------|
|
||||||
| `gpg_private_key` | String | GPG private key exported as an ASCII armored version or its base64 encoding (**required**) |
|
| `gpg_private_key` | String | GPG private key exported as an ASCII armored version or its base64 encoding (**required**) |
|
||||||
| `passphrase` | String | Passphrase of the GPG private key |
|
| `passphrase` | String | Passphrase of the GPG private key |
|
||||||
| `git_config_global` | Bool | Set Git config global (default `false`) |
|
| `trust_level` | String | Set key's trust level |
|
||||||
| `git_user_signingkey` | Bool | Set GPG signing keyID for this Git repository (default `false`) |
|
| `git_config_global` | Bool | Set Git config global (default `false`) |
|
||||||
| `git_commit_gpgsign` | Bool | Sign all commits automatically. (default `false`) |
|
| `git_user_signingkey` | Bool | Set GPG signing keyID for this Git repository (default `false`) |
|
||||||
| `git_tag_gpgsign` | Bool | Sign all tags automatically. (default `false`) |
|
| `git_commit_gpgsign` | Bool | Sign all commits automatically. (default `false`) |
|
||||||
| `git_push_gpgsign` | String | Sign all pushes automatically. (default `if-asked`) |
|
| `git_tag_gpgsign` | Bool | Sign all tags automatically. (default `false`) |
|
||||||
| `git_committer_name` | String | Set commit author's name (defaults to the name associated with the GPG key) |
|
| `git_push_gpgsign` | String | Sign all pushes automatically. (default `if-asked`) |
|
||||||
| `git_committer_email` | String | Set commit author's email (defaults to the email address associated with the GPG key) |
|
| `git_committer_name` | String | Set commit author's name (defaults to the name associated with the GPG key) |
|
||||||
| `workdir` | String | Working directory (below repository root) (default `.`) |
|
| `git_committer_email` | String | Set commit author's email (defaults to the email address associated with the GPG key) |
|
||||||
| `fingerprint` | String | Specific fingerprint to use (subkey) |
|
| `workdir` | String | Working directory (below repository root) (default `.`) |
|
||||||
|
| `fingerprint` | String | Specific fingerprint to use (subkey) |
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
> `git_user_signingkey` needs to be enabled for `git_commit_gpgsign`, `git_tag_gpgsign`,
|
> `git_user_signingkey` needs to be enabled for `git_commit_gpgsign`, `git_tag_gpgsign`,
|
||||||
> `git_push_gpgsign`, `git_committer_name`, `git_committer_email` inputs.
|
> `git_push_gpgsign`, `git_committer_name`, `git_committer_email` inputs.
|
||||||
|
|
||||||
|
@ -191,12 +227,12 @@ 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 | Name associated with the GPG key |
|
||||||
| `email` | String | Email address associated with the GPG key |
|
| `email` | String | Email address associated with the GPG key |
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
|
@ -107,10 +107,10 @@ for (const userInfo of userInfos) {
|
||||||
describe('getKeygrip', () => {
|
describe('getKeygrip', () => {
|
||||||
it('returns the keygrip for a given fingerprint', async () => {
|
it('returns the keygrip for a given fingerprint', async () => {
|
||||||
await gpg.importKey(userInfo.pgp);
|
await gpg.importKey(userInfo.pgp);
|
||||||
for (const [i, fingerprint] of userInfo.fingerprints.entries()) {
|
for (const {idx, fingerprint} of userInfo.fingerprints.map((fingerprint, idx) => ({idx, fingerprint}))) {
|
||||||
await gpg.getKeygrip(fingerprint).then(keygrip => {
|
await gpg.getKeygrip(fingerprint).then(keygrip => {
|
||||||
expect(keygrip.length).toEqual(userInfo.keygrips[i].length);
|
expect(keygrip.length).toEqual(userInfo.keygrips[idx].length);
|
||||||
expect(keygrip).toEqual(userInfo.keygrips[i]);
|
expect(keygrip).toEqual(userInfo.keygrips[idx]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -128,6 +128,16 @@ for (const userInfo of userInfos) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('setTrustLevel', () => {
|
||||||
|
it('set trust level', async () => {
|
||||||
|
await gpg.importKey(userInfo.pgp);
|
||||||
|
await gpg.configureAgent(gpg.agentConfig);
|
||||||
|
expect(() => {
|
||||||
|
gpg.setTrustLevel(userInfo.keyID, '5');
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('deleteKey', () => {
|
describe('deleteKey', () => {
|
||||||
// eslint-disable-next-line jest/expect-expect
|
// eslint-disable-next-line jest/expect-expect
|
||||||
it('removes key from GnuPG', async () => {
|
it('removes key from GnuPG', async () => {
|
||||||
|
|
|
@ -13,6 +13,9 @@ inputs:
|
||||||
passphrase:
|
passphrase:
|
||||||
description: 'Passphrase of the GPG private key'
|
description: 'Passphrase of the GPG private key'
|
||||||
required: false
|
required: false
|
||||||
|
trust_level:
|
||||||
|
description: "Set key's trust level"
|
||||||
|
required: false
|
||||||
git_config_global:
|
git_config_global:
|
||||||
description: 'Set Git config global'
|
description: 'Set Git config global'
|
||||||
default: 'false'
|
default: 'false'
|
||||||
|
|
|
@ -3,6 +3,7 @@ import * as core from '@actions/core';
|
||||||
export interface Inputs {
|
export interface Inputs {
|
||||||
gpgPrivateKey: string;
|
gpgPrivateKey: string;
|
||||||
passphrase: string;
|
passphrase: string;
|
||||||
|
trustLevel: string;
|
||||||
gitConfigGlobal: boolean;
|
gitConfigGlobal: boolean;
|
||||||
gitUserSigningkey: boolean;
|
gitUserSigningkey: boolean;
|
||||||
gitCommitGpgsign: boolean;
|
gitCommitGpgsign: boolean;
|
||||||
|
@ -18,6 +19,7 @@ export async function getInputs(): Promise<Inputs> {
|
||||||
return {
|
return {
|
||||||
gpgPrivateKey: core.getInput('gpg_private_key', {required: true}),
|
gpgPrivateKey: core.getInput('gpg_private_key', {required: true}),
|
||||||
passphrase: core.getInput('passphrase'),
|
passphrase: core.getInput('passphrase'),
|
||||||
|
trustLevel: core.getInput('trust_level'),
|
||||||
gitConfigGlobal: core.getBooleanInput('git_config_global'),
|
gitConfigGlobal: core.getBooleanInput('git_config_global'),
|
||||||
gitUserSigningkey: core.getBooleanInput('git_user_signingkey'),
|
gitUserSigningkey: core.getBooleanInput('git_user_signingkey'),
|
||||||
gitCommitGpgsign: core.getBooleanInput('git_commit_gpgsign'),
|
gitCommitGpgsign: core.getBooleanInput('git_commit_gpgsign'),
|
||||||
|
|
14
src/gpg.ts
14
src/gpg.ts
|
@ -206,6 +206,20 @@ export const presetPassphrase = async (keygrip: string, passphrase: string): Pro
|
||||||
return await gpgConnectAgent(`KEYINFO ${keygrip}`);
|
return await gpgConnectAgent(`KEYINFO ${keygrip}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setTrustLevel = async (keyID: string, trust: string): Promise<void> => {
|
||||||
|
await exec
|
||||||
|
.getExecOutput('gpg', ['--batch', '--no-tty', '--command-fd', '0', '--edit-key', keyID], {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true,
|
||||||
|
input: Buffer.from(`trust\n${trust}\ny\nquit\n`)
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||||
|
throw new Error(res.stderr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const deleteKey = async (fingerprint: string): Promise<void> => {
|
export const deleteKey = async (fingerprint: string): Promise<void> => {
|
||||||
await exec
|
await exec
|
||||||
.getExecOutput('gpg', ['--batch', '--yes', '--delete-secret-keys', fingerprint], {
|
.getExecOutput('gpg', ['--batch', '--yes', '--delete-secret-keys', fingerprint], {
|
||||||
|
|
|
@ -81,6 +81,14 @@ async function run(): Promise<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inputs.trustLevel) {
|
||||||
|
await core.group(`Setting key's trust level`, async () => {
|
||||||
|
await gpg.setTrustLevel(privateKey.keyID, inputs.trustLevel).then(() => {
|
||||||
|
core.info(`Trust level set to ${inputs.trustLevel} for ${privateKey.keyID}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await core.group(`Setting outputs`, async () => {
|
await core.group(`Setting outputs`, async () => {
|
||||||
core.info(`fingerprint=${fingerprint}`);
|
core.info(`fingerprint=${fingerprint}`);
|
||||||
core.setOutput('fingerprint', fingerprint);
|
core.setOutput('fingerprint', fingerprint);
|
||||||
|
|
Loading…
Reference in a new issue