Skip to content

Commit 0e10e07

Browse files
feat: cover signed commit flows for action-commit-push
Extend the action-commit-push E2E workflow with GPG- and SSH-signed commit scenarios that generate ephemeral keys and verify the resulting signatures. Update Triglav documentation to reflect the added regression coverage.
1 parent 82a063a commit 0e10e07

2 files changed

Lines changed: 162 additions & 10 deletions

File tree

.github/workflows/e2e-action-commit-push.yml

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,3 +503,154 @@ jobs:
503503
if git ls-remote --exit-code --heads origin "${branch}" >/dev/null 2>&1; then
504504
git push origin --delete "${branch}"
505505
fi
506+
507+
gpg-signed-commit:
508+
name: GPG-signed commit
509+
needs: [preflight]
510+
runs-on: ubuntu-latest
511+
steps:
512+
- name: Checkout repository
513+
uses: actions/checkout@v6
514+
with:
515+
fetch-depth: 0
516+
517+
- name: Generate GPG signing material
518+
if: ${{ inputs.mode == 'ref' }}
519+
id: gpg
520+
run: |
521+
set -euo pipefail
522+
export GNUPGHOME
523+
GNUPGHOME="$(mktemp -d)"
524+
chmod 700 "${GNUPGHOME}"
525+
cat > gpg-batch <<EOF
526+
Key-Type: RSA
527+
Key-Length: 2048
528+
Name-Real: Triglav E2E
529+
Name-Email: ${{ github.actor }}@users.noreply.github.com
530+
Passphrase: triglav-passphrase
531+
Expire-Date: 0
532+
%commit
533+
EOF
534+
gpg --batch --generate-key gpg-batch
535+
gpg --batch --pinentry-mode loopback --passphrase triglav-passphrase --armor --export-secret-keys "${{ github.actor }}@users.noreply.github.com" > gpg-private.asc
536+
gpg --armor --export "${{ github.actor }}@users.noreply.github.com" > gpg-public.asc
537+
{
538+
echo "secret_key<<EOF"
539+
cat gpg-private.asc
540+
echo "EOF"
541+
echo "public_key<<EOF"
542+
cat gpg-public.asc
543+
echo "EOF"
544+
} >> "${GITHUB_OUTPUT}"
545+
546+
- name: Create test file
547+
run: echo "E2E gpg signing test $(date -u)" > e2e-gpg-signing.md
548+
549+
- name: Commit and push signed changes with GPG
550+
if: ${{ inputs.mode == 'ref' }}
551+
id: gpg_commit
552+
uses: devops-infra/action-commit-push@master
553+
with:
554+
github_token: ${{ secrets.GITHUB_TOKEN }}
555+
commit_message: "test(commit-push): gpg signed commit"
556+
target_branch: test/e2e-commit-push-gpg-${{ github.run_id }}
557+
signing_mode: gpg
558+
signing_key: ${{ steps.gpg.outputs.secret_key }}
559+
signing_passphrase: triglav-passphrase
560+
561+
- name: GPG signing via docker image (preview)
562+
if: ${{ inputs.mode == 'image' }}
563+
run: |
564+
if [ -z "${{ inputs.image_tag }}" ]; then
565+
echo "image_tag is required when mode=image"
566+
exit 1
567+
fi
568+
echo "Image mode placeholder for devopsinfra/action-commit-push:${{ inputs.image_tag }}"
569+
echo "Use ref mode for authoritative validation until image-mode harness is finalized."
570+
571+
- name: Verify GPG commit signature
572+
if: ${{ inputs.mode == 'ref' }}
573+
run: |
574+
set -euo pipefail
575+
export GNUPGHOME
576+
GNUPGHOME="$(mktemp -d)"
577+
chmod 700 "${GNUPGHOME}"
578+
printf '%s\n' "${{ steps.gpg.outputs.public_key }}" > gpg-public.asc
579+
gpg --import gpg-public.asc >/dev/null 2>&1
580+
git fetch origin "test/e2e-commit-push-gpg-${{ github.run_id }}"
581+
git verify-commit "origin/test/e2e-commit-push-gpg-${{ github.run_id }}"
582+
583+
- name: Cleanup - delete test branch
584+
if: ${{ always() && inputs.mode == 'ref' }}
585+
run: |
586+
branch="test/e2e-commit-push-gpg-${{ github.run_id }}"
587+
if git ls-remote --exit-code --heads origin "${branch}" >/dev/null 2>&1; then
588+
git push origin --delete "${branch}"
589+
fi
590+
591+
ssh-signed-commit:
592+
name: SSH-signed commit
593+
needs: [preflight]
594+
runs-on: ubuntu-latest
595+
steps:
596+
- name: Checkout repository
597+
uses: actions/checkout@v6
598+
with:
599+
fetch-depth: 0
600+
601+
- name: Generate SSH signing material
602+
if: ${{ inputs.mode == 'ref' }}
603+
id: ssh
604+
run: |
605+
set -euo pipefail
606+
ssh-keygen -q -t ed25519 -N '' -C "${{ github.actor }}@users.noreply.github.com" -f ssh-signing-key
607+
{
608+
echo "private_key<<EOF"
609+
cat ssh-signing-key
610+
echo "EOF"
611+
echo "public_key<<EOF"
612+
cat ssh-signing-key.pub
613+
echo "EOF"
614+
} >> "${GITHUB_OUTPUT}"
615+
616+
- name: Create test file
617+
run: echo "E2E ssh signing test $(date -u)" > e2e-ssh-signing.md
618+
619+
- name: Commit and push signed changes with SSH
620+
if: ${{ inputs.mode == 'ref' }}
621+
id: ssh_commit
622+
uses: devops-infra/action-commit-push@master
623+
with:
624+
github_token: ${{ secrets.GITHUB_TOKEN }}
625+
commit_message: "test(commit-push): ssh signed commit"
626+
target_branch: test/e2e-commit-push-ssh-${{ github.run_id }}
627+
signing_mode: ssh
628+
signing_key: ${{ steps.ssh.outputs.private_key }}
629+
630+
- name: SSH signing via docker image (preview)
631+
if: ${{ inputs.mode == 'image' }}
632+
run: |
633+
if [ -z "${{ inputs.image_tag }}" ]; then
634+
echo "image_tag is required when mode=image"
635+
exit 1
636+
fi
637+
echo "Image mode placeholder for devopsinfra/action-commit-push:${{ inputs.image_tag }}"
638+
echo "Use ref mode for authoritative validation until image-mode harness is finalized."
639+
640+
- name: Verify SSH commit signature
641+
if: ${{ inputs.mode == 'ref' }}
642+
run: |
643+
set -euo pipefail
644+
printf '%s %s\n' "${{ github.actor }}@users.noreply.github.com" "${{ steps.ssh.outputs.public_key }}" > allowed_signers
645+
git config gpg.format ssh
646+
git config gpg.ssh.allowedSignersFile "$PWD/allowed_signers"
647+
git fetch origin "test/e2e-commit-push-ssh-${{ github.run_id }}"
648+
git verify-commit "origin/test/e2e-commit-push-ssh-${{ github.run_id }}"
649+
650+
- name: Cleanup - delete test branch
651+
if: ${{ always() && inputs.mode == 'ref' }}
652+
run: |
653+
branch="test/e2e-commit-push-ssh-${{ github.run_id }}"
654+
if git ls-remote --exit-code --heads origin "${branch}" >/dev/null 2>&1; then
655+
git push origin --delete "${branch}"
656+
fi

README.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ In Slavic mythology, Triglav represents three realms. That maps well to this fra
2222

2323
## Covered Actions and Test Types
2424

25-
| Action | Workflow | Test Coverage |
26-
|------------------------------------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
27-
| `devops-infra/action-commit-push` | `.github/workflows/e2e-action-commit-push.yml` | branch creation/push, custom message/prefix, empty commit mode, amend with force-with-lease, output verification, cleanup |
28-
| `devops-infra/action-pull-request` | `.github/workflows/e2e-action-pull-request.yml` | PR creation/update paths, custom title/body, draft + `get_diff`, `repository` + `repository_path`, output verification, cleanup |
29-
| `devops-infra/action-format-hcl` | `.github/workflows/e2e-action-format-hcl.yml` | check mode pass/fail, write mode, list/diff mode, malformed input detection |
30-
| `devops-infra/action-container-structure-test` | `.github/workflows/e2e-action-container-structure-test.yml` | text/json/junit output modes, report file creation, multi-config execution, output counters |
31-
| `devops-infra/action-terraform-copy-vars` | `.github/workflows/e2e-action-terraform-copy-vars.yml` | variable propagation across modules, custom path inputs, strict missing-variable failure mode |
32-
| `devops-infra/action-terraform-validate` | `.github/workflows/e2e-action-terraform-validate.yml` | valid module validation, scoped validation via `dir_filter` |
33-
| `devops-infra/action-tflint` | `.github/workflows/e2e-action-tflint.yml` | lint execution across modules, scoped lint via `dir_filter`, non-blocking findings mode |
34-
| `devops-infra/template-action` | `.github/workflows/e2e-action-template-action.yml` | baseline template behavior validation, output contract checks, debug-mode execution |
25+
| Action | Workflow | Test Coverage |
26+
|------------------------------------------------|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
27+
| `devops-infra/action-commit-push` | `.github/workflows/e2e-action-commit-push.yml` | branch creation/push, custom message/prefix, empty commit mode, amend with force-with-lease, GPG and SSH signed commits, output verification, cleanup |
28+
| `devops-infra/action-pull-request` | `.github/workflows/e2e-action-pull-request.yml` | PR creation/update paths, custom title/body, draft + `get_diff`, `repository` + `repository_path`, output verification, cleanup |
29+
| `devops-infra/action-format-hcl` | `.github/workflows/e2e-action-format-hcl.yml` | check mode pass/fail, write mode, list/diff mode, malformed input detection |
30+
| `devops-infra/action-container-structure-test` | `.github/workflows/e2e-action-container-structure-test.yml` | text/json/junit output modes, report file creation, multi-config execution, output counters |
31+
| `devops-infra/action-terraform-copy-vars` | `.github/workflows/e2e-action-terraform-copy-vars.yml` | variable propagation across modules, custom path inputs, strict missing-variable failure mode |
32+
| `devops-infra/action-terraform-validate` | `.github/workflows/e2e-action-terraform-validate.yml` | valid module validation, scoped validation via `dir_filter` |
33+
| `devops-infra/action-tflint` | `.github/workflows/e2e-action-tflint.yml` | lint execution across modules, scoped lint via `dir_filter`, non-blocking findings mode |
34+
| `devops-infra/template-action` | `.github/workflows/e2e-action-template-action.yml` | baseline template behavior validation, output contract checks, debug-mode execution |
3535

3636
## Workflow Orchestration
3737

@@ -155,4 +155,5 @@ jobs:
155155
## Notes
156156
157157
- E2E workflows intentionally create temporary test branches and pull requests and then clean them up.
158+
- The `action-commit-push` E2E workflow generates ephemeral GPG and SSH keys during the run and verifies the resulting commit signatures against the matching public keys.
158159
- Use this repository to validate behavior before promoting changes in action repositories or reusable org workflows.

0 commit comments

Comments
 (0)