Skip to content

Commit 05fea50

Browse files
committed
input to set private key trust level
1 parent ff08495 commit 05fea50

File tree

7 files changed

+159
-27
lines changed

7 files changed

+159
-27
lines changed

.github/workflows/ci.yml

+61-2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ jobs:
5555
if (!fs.existsSync(gnupgfolder)){
5656
fs.mkdirSync(gnupgfolder);
5757
}
58+
fs.chmodSync(gnupgfolder, '0700');
5859
fs.copyFile('__tests__/fixtures/gpg.conf', `${gnupgfolder}/gpg.conf`, (err) => {
5960
if (err) throw err;
6061
});
@@ -69,11 +70,11 @@ jobs:
6970
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
7071
-
7172
name: Import GPG
72-
id: import_gpg
7373
uses: ./
7474
with:
7575
gpg_private_key: ${{ steps.test.outputs.pgp }}
7676
passphrase: ${{ steps.test.outputs.passphrase }}
77+
trust_level: 5
7778
git_config_global: ${{ matrix.global }}
7879
git_user_signingkey: true
7980
git_commit_gpgsign: true
@@ -116,7 +117,6 @@ jobs:
116117
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
117118
-
118119
name: Import GPG
119-
id: import_gpg
120120
uses: ./
121121
with:
122122
gpg_private_key: ${{ steps.test.outputs.pgp-base64 }}
@@ -126,3 +126,62 @@ jobs:
126126
git_tag_gpgsign: true
127127
git_push_gpgsign: if-asked
128128
fingerprint: ${{ matrix.fingerprint }}
129+
130+
trust:
131+
runs-on: ${{ matrix.os }}
132+
strategy:
133+
fail-fast: false
134+
matrix:
135+
key:
136+
- test-key
137+
level:
138+
- ''
139+
- 5
140+
- 4
141+
- 3
142+
- 2
143+
- 1
144+
os:
145+
- ubuntu-latest
146+
- macOS-latest
147+
- windows-latest
148+
steps:
149+
-
150+
name: Checkout
151+
uses: actions/checkout@v3
152+
-
153+
name: GPG conf
154+
uses: actions/github-script@v6
155+
with:
156+
script: |
157+
const fs = require('fs');
158+
const gnupgfolder = `${require('os').homedir()}/.gnupg`;
159+
if (!fs.existsSync(gnupgfolder)){
160+
fs.mkdirSync(gnupgfolder);
161+
}
162+
fs.chmodSync(gnupgfolder, '0700');
163+
fs.copyFile('__tests__/fixtures/gpg.conf', `${gnupgfolder}/gpg.conf`, (err) => {
164+
if (err) throw err;
165+
});
166+
-
167+
name: Get test key and passphrase
168+
uses: actions/github-script@v6
169+
id: test
170+
with:
171+
script: |
172+
const fs = require('fs');
173+
core.setOutput('pgp', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pgp', {encoding: 'utf8'}));
174+
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
175+
-
176+
name: Import GPG
177+
id: import_gpg
178+
uses: ./
179+
with:
180+
gpg_private_key: ${{ steps.test.outputs.pgp }}
181+
passphrase: ${{ steps.test.outputs.passphrase }}
182+
trust_level: ${{ matrix.level }}
183+
-
184+
name: List trust values
185+
run: |
186+
gpg --export-ownertrust
187+
shell: bash

README.md

+58-22
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ ___
1919
* [Workflow](#workflow)
2020
* [Sign commits](#sign-commits)
2121
* [Use a subkey](#use-a-subkey)
22+
* [Set key's trust level](#set-keys-trust-level)
2223
* [Customizing](#customizing)
2324
* [inputs](#inputs)
2425
* [outputs](#outputs)
@@ -76,7 +77,6 @@ jobs:
7677
uses: actions/checkout@v3
7778
-
7879
name: Import GPG key
79-
id: import_gpg
8080
uses: crazy-max/ghaction-import-gpg@v5
8181
with:
8282
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
@@ -139,7 +139,6 @@ jobs:
139139
uses: actions/checkout@v3
140140
-
141141
name: Import GPG key
142-
id: import_gpg
143142
uses: crazy-max/ghaction-import-gpg@v5
144143
with:
145144
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
@@ -164,39 +163,76 @@ sub ed25519 2021-09-24 [S]
164163

165164
You can use the subkey with signing capability whose fingerprint is `C17D11ADF199F12A30A0910F1F80449BE0B08CB8`.
166165

166+
### Set key's trust level
167+
168+
With the `trust_level` input, you can specify the trust level of the GPG key.
169+
170+
Valid values are:
171+
* `1`: unknown
172+
* `2`: never
173+
* `3`: marginal
174+
* `4`: full
175+
* `5`: ultimate
176+
177+
```yaml
178+
name: import-gpg
179+
180+
on:
181+
push:
182+
branches: master
183+
184+
jobs:
185+
import-gpg:
186+
runs-on: ubuntu-latest
187+
steps:
188+
-
189+
name: Checkout
190+
uses: actions/checkout@v3
191+
-
192+
name: Import GPG key
193+
uses: crazy-max/ghaction-import-gpg@v5
194+
with:
195+
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
196+
passphrase: ${{ secrets.PASSPHRASE }}
197+
trust_level: 5
198+
```
199+
167200
## Customizing
168201

169202
### inputs
170203

171204
Following inputs can be used as `step.with` keys
172205

173-
| Name | Type | Description |
174-
|---------------------------------------|---------|------------------------------------------------|
175-
| `gpg_private_key` | String | GPG private key exported as an ASCII armored version or its base64 encoding (**required**) |
176-
| `passphrase` | String | Passphrase of the GPG private key |
177-
| `git_config_global` | Bool | Set Git config global (default `false`) |
178-
| `git_user_signingkey` | Bool | Set GPG signing keyID for this Git repository (default `false`) |
179-
| `git_commit_gpgsign` | Bool | Sign all commits automatically. (default `false`) |
180-
| `git_tag_gpgsign` | Bool | Sign all tags automatically. (default `false`) |
181-
| `git_push_gpgsign` | String | Sign all pushes automatically. (default `if-asked`) |
182-
| `git_committer_name` | String | Set commit author's name (defaults to the name associated with the GPG key) |
183-
| `git_committer_email` | String | Set commit author's email (defaults to the email address associated with the GPG key) |
184-
| `workdir` | String | Working directory (below repository root) (default `.`) |
185-
| `fingerprint` | String | Specific fingerprint to use (subkey) |
186-
206+
| Name | Type | Description |
207+
|-----------------------|--------|--------------------------------------------------------------------------------------------|
208+
| `gpg_private_key` | String | GPG private key exported as an ASCII armored version or its base64 encoding (**required**) |
209+
| `passphrase` | String | Passphrase of the GPG private key |
210+
| `trust_level` | String | Set key's trust level |
211+
| `git_config_global` | Bool | Set Git config global (default `false`) |
212+
| `git_user_signingkey` | Bool | Set GPG signing keyID for this Git repository (default `false`) |
213+
| `git_commit_gpgsign` | Bool | Sign all commits automatically. (default `false`) |
214+
| `git_tag_gpgsign` | Bool | Sign all tags automatically. (default `false`) |
215+
| `git_push_gpgsign` | String | Sign all pushes automatically. (default `if-asked`) |
216+
| `git_committer_name` | String | Set commit author's name (defaults to the name associated with the GPG key) |
217+
| `git_committer_email` | String | Set commit author's email (defaults to the email address associated with the GPG key) |
218+
| `workdir` | String | Working directory (below repository root) (default `.`) |
219+
| `fingerprint` | String | Specific fingerprint to use (subkey) |
220+
221+
> **Note**
222+
>
187223
> `git_user_signingkey` needs to be enabled for `git_commit_gpgsign`, `git_tag_gpgsign`,
188224
> `git_push_gpgsign`, `git_committer_name`, `git_committer_email` inputs.
189225

190226
### outputs
191227

192228
Following outputs are available
193229

194-
| Name | Type | Description |
195-
|---------------|---------|---------------------------------------|
196-
| `fingerprint` | String | Fingerprint of the GPG key (recommended as [user ID](https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html)) |
197-
| `keyid` | String | Low 64 bits of the X.509 certificate SHA-1 fingerprint |
198-
| `name` | String | Name associated with the GPG key |
199-
| `email` | String | Email address associated with the GPG key |
230+
| Name | Type | Description |
231+
|---------------|--------|---------------------------------------------------------------------------------------------------------------------------------|
232+
| `fingerprint` | String | Fingerprint of the GPG key (recommended as [user ID](https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html)) |
233+
| `keyid` | String | Low 64 bits of the X.509 certificate SHA-1 fingerprint |
234+
| `name` | String | Name associated with the GPG key |
235+
| `email` | String | Email address associated with the GPG key |
200236

201237
## Contributing
202238

__tests__/gpg.test.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ for (const userInfo of userInfos) {
107107
describe('getKeygrip', () => {
108108
it('returns the keygrip for a given fingerprint', async () => {
109109
await gpg.importKey(userInfo.pgp);
110-
for (const [i, fingerprint] of userInfo.fingerprints.entries()) {
110+
for (const {idx, fingerprint} of userInfo.fingerprints.map((fingerprint, idx) => ({idx, fingerprint}))) {
111111
await gpg.getKeygrip(fingerprint).then(keygrip => {
112-
expect(keygrip.length).toEqual(userInfo.keygrips[i].length);
113-
expect(keygrip).toEqual(userInfo.keygrips[i]);
112+
expect(keygrip.length).toEqual(userInfo.keygrips[idx].length);
113+
expect(keygrip).toEqual(userInfo.keygrips[idx]);
114114
});
115115
}
116116
});
@@ -128,6 +128,16 @@ for (const userInfo of userInfos) {
128128
});
129129
});
130130

131+
describe('setTrustLevel', () => {
132+
it('set trust level', async () => {
133+
await gpg.importKey(userInfo.pgp);
134+
await gpg.configureAgent(gpg.agentConfig);
135+
expect(() => {
136+
gpg.setTrustLevel(userInfo.keyID, '5');
137+
}).not.toThrow();
138+
});
139+
});
140+
131141
describe('deleteKey', () => {
132142
// eslint-disable-next-line jest/expect-expect
133143
it('removes key from GnuPG', async () => {

action.yml

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ inputs:
1313
passphrase:
1414
description: 'Passphrase of the GPG private key'
1515
required: false
16+
trust_level:
17+
description: "Set key's trust level"
18+
required: false
1619
git_config_global:
1720
description: 'Set Git config global'
1821
default: 'false'

src/context.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as core from '@actions/core';
33
export interface Inputs {
44
gpgPrivateKey: string;
55
passphrase: string;
6+
trustLevel: string;
67
gitConfigGlobal: boolean;
78
gitUserSigningkey: boolean;
89
gitCommitGpgsign: boolean;
@@ -18,6 +19,7 @@ export async function getInputs(): Promise<Inputs> {
1819
return {
1920
gpgPrivateKey: core.getInput('gpg_private_key', {required: true}),
2021
passphrase: core.getInput('passphrase'),
22+
trustLevel: core.getInput('trust_level'),
2123
gitConfigGlobal: core.getBooleanInput('git_config_global'),
2224
gitUserSigningkey: core.getBooleanInput('git_user_signingkey'),
2325
gitCommitGpgsign: core.getBooleanInput('git_commit_gpgsign'),

src/gpg.ts

+14
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,20 @@ export const presetPassphrase = async (keygrip: string, passphrase: string): Pro
206206
return await gpgConnectAgent(`KEYINFO ${keygrip}`);
207207
};
208208

209+
export const setTrustLevel = async (keyID: string, trust: string): Promise<void> => {
210+
await exec
211+
.getExecOutput('gpg', ['--batch', '--no-tty', '--command-fd', '0', '--edit-key', keyID], {
212+
ignoreReturnCode: true,
213+
silent: true,
214+
input: Buffer.from(`trust\n${trust}\ny\nquit\n`)
215+
})
216+
.then(res => {
217+
if (res.stderr.length > 0 && res.exitCode != 0) {
218+
throw new Error(res.stderr);
219+
}
220+
});
221+
};
222+
209223
export const deleteKey = async (fingerprint: string): Promise<void> => {
210224
await exec
211225
.getExecOutput('gpg', ['--batch', '--yes', '--delete-secret-keys', fingerprint], {

src/main.ts

+8
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ async function run(): Promise<void> {
8181
});
8282
}
8383

84+
if (inputs.trustLevel) {
85+
await core.group(`Setting key's trust level`, async () => {
86+
await gpg.setTrustLevel(privateKey.keyID, inputs.trustLevel).then(() => {
87+
core.info(`Trust level set to ${inputs.trustLevel} for ${privateKey.keyID}`);
88+
});
89+
});
90+
}
91+
8492
await core.group(`Setting outputs`, async () => {
8593
core.info(`fingerprint=${fingerprint}`);
8694
core.setOutput('fingerprint', fingerprint);

0 commit comments

Comments
 (0)