Skip to content

Commit

Permalink
feat: add gpg signing
Browse files Browse the repository at this point in the history
This commit allows users to sign the tags using a GPG key.
  • Loading branch information
rickstaa committed Nov 8, 2023
1 parent 90b11eb commit 76674de
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 54 deletions.
6 changes: 4 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
FROM alpine:3.18

RUN apk --no-cache add git && \
rm -rf /var/lib/apt/lists/*
RUN apk --no-cache add \
git \
gnupg && \
rm -rf /var/cache/apk/*

COPY entrypoint.sh /entrypoint.sh

Expand Down
118 changes: 76 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,99 @@

[![Docker Image CI](https://github.com/rickstaa/action-update-semver/workflows/Docker%20Image%20CI/badge.svg)](https://github.com/rickstaa/action-update-semver/actions)
[![Code quality CI](https://github.com/rickstaa/action-update-semver/workflows/Code%20quality%20CI/badge.svg)](https://github.com/rickstaa/action-update-semver/actions?query=workflow%3A%22Code+quality+CI%22)
[![release](https://github.com/rickstaa/action-update-semver/workflows/release/badge.svg)](https://github.com/rickstaa/action-update-semver/actions?query=workflow%3Arelease)
[![Release](https://github.com/rickstaa/action-update-semver/workflows/release/badge.svg)](https://github.com/rickstaa/action-update-semver/actions?query=workflow%3Arelease)
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/rickstaa/action-update-semver?logo=github&sort=semver)](https://github.com/rickstaa/action-update-semver/releases)

This action updates major/minor release tags on a tag push. e.g. Update `v1` and `v1.2` tag when
released `v1.2.3`. It can also be used to move the patch version up to the latest commit.
This GitHub Action simplifies the process of updating major/minor release tags on a tag push. For example, it automatically updates both `v1` and `v1.2` tags when releasing version `v1.2.3`. Additionally, it provides the option to move the patch version up to the latest commit, making it convenient for use with tools like [auto-changelog](https://www.npmjs.com/package/auto-changelog) to automatically generate changelogs for your releases.

It works well for GitHub Action. ref: <https://help.github.com/en/articles/about-actions#versioning-your-action>
It's designed to seamlessly integrate with GitHub Actions. For more details on versioning your action, refer to [GitHub Actions documentation](https://help.github.com/en/articles/about-actions#versioning-your-action).

## Inputs

### `tag`

**Optional**. Existing tag to update from. Default comes from `$GITHUB_REF`.
**Optional**. Specifies the existing tag to update from. Defaults to `$GITHUB_REF`.

### `message`

**Optional**. Tag message. Default: `Release $TAG`
**Optional**. Custom tag message. Default: `Release $TAG`.

### `major_version_tag_only`

**Optional**. Create only major version tags. Default: `false`
**Optional**. Creates only major version tags. Default: `false`.

### `move_patch_tag`

**Optional**. Moves the existing tag to the latest commit inside the github action. Default: `false`. Useful when you want to use [auto-changelog](https://www.npmjs.com/package/auto-changelog) to automatically add a changelog to your release.

| ⚠️ | In order to prevent unexpected changes this only works when you explicitly specified a tag. |
| --- | ------------------------------------------------------------------------------------------- |
**Optional**. Moves the existing tag to the latest commit inside the GitHub Action. Default: `false`. Note that this only works when you explicitly specify a tag to prevent unexpected changes.

### `github_token`

**Optional**. It's no need to specify it if you use checkout@v2. Required for
checkout@v1 action.
**Optional**. Only required for checkout@v1 action; otherwise, it's not necessary if you use checkout@v2 or higher.

### `gpg_private_key`

**Optional**. Specifies the GPG private key to sign the tag with. Default: `""`.

### `gpg_passphrase`

**Optional**. Specifies the GPG passphrase to sign the tag with. Default: `""`.

## Example Usage

### Simple example

```yaml
name: Update Semver
on:
push:
branches-ignore:
- '**'
tags:
- 'v*.*.*'
jobs:
update-semver:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: rickstaa/action-update-semver@v1
with:
major_version_tag_only: true # (optional, default is "false")
```

Certainly! Here's a refined version of your documentation:

### Signing Tags with GPG

To sign tags with GPG, follow these steps:

#### 1. Generate a GPG Key

First, [generate a GPG key](https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key). Once generated, export the GPG private key in ASCII armored format to your clipboard using one of the following commands based on your operating system:

- **macOS:**
```shell
gpg --armor --export-secret-key joe@foo.bar | pbcopy
```

- **Ubuntu (GNU base64):**
```shell
gpg --armor --export-secret-key joe@foo.bar -w0 | xclip
```

- **Arch:**
```shell
gpg --armor --export-secret-key joe@foo.bar | xclip -selection clipboard -i
```

- **FreeBSD (BSD base64):**
```shell
gpg --armor --export-s[.github/workflows/update_semver.yml](.github/workflows/update_semver.yml)e your GPG passphrase.

## Example usage
#### 3. Update Workflow YAML

### [.github/workflows/update_semver.yml](.github/workflows/update_semver.yml)
Modify your workflow YAML file to include the GPG private key and passphrase in the `gpg_private_key` and `gpg_passphrase` inputs:

```yml
```yaml
name: Update Semver
on:
push:
Expand All @@ -52,40 +106,20 @@ jobs:
update-semver:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: rickstaa/action-update-semver@v1
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg_passphrase: ${{ secrets.PASSPHRASE }}
major_version_tag_only: true # (optional, default is "false")
```

<details>

<summary>oneliner</summary>

$ cat <<EOF > .github/workflows/update_semver.yml
name: Update Semver
on:
push:
branches-ignore:
- '**'
tags:
- 'v*.*.*'
jobs:
update-semver:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: rickstaa/action-update-semver@v1
with:
github_token: \${{ secrets.github_token }}
EOF

</details>
This workflow will now sign tags using the specified GPG key during tag creation.

## Contributing

Feel free to open an issue if you have ideas on how to make this GitHub action better or if you want to report a bug! All contributions are welcome. :rocket: Please consult the [contribution guideliness](CONTRIBUTING.md) for more information.
Feel free to open an issue if you have ideas on how to improve this GitHub Action or if you want to report a bug! All contributions are welcome. :rocket: Please consult the [contribution guidelines](CONTRIBUTING.md) for more information.

## Acknowledgement
## Acknowledgment

This action is based on [@haya14busa's](https://github.com/haya14busa/) [update-major-minor-semver](https://github.com/marketplace/actions/update-major-minor-semver).
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ inputs:
move_patch_tag:
description: "Optional. Move the patch tag to the latest commit."
required: false
gpg_private_key:
description: "Optional. GPG key to sign the tag with."
required: false
gpg_passphrase:
description: "Optional. GPG key passphrase."
required: false
runs:
using: "docker"
image: "Dockerfile"
Expand Down
49 changes: 39 additions & 10 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,47 +1,76 @@
#!/bin/sh
set -eu

cd "${GITHUB_WORKSPACE}" || exit

# Apply hotfix for 'fatal: unsafe repository' error (see #13).
git config --global --add safe.directory "${GITHUB_WORKSPACE}"

cd "${GITHUB_WORKSPACE}" || exit

# Set up variables.
TAG="${INPUT_TAG:-${GITHUB_REF#refs/tags/}}" # v1.2.3
MINOR="${TAG%.*}" # v1.2
MAJOR="${MINOR%.*}" # v1
MAJOR_VERSION_TAG_ONLY=${INPUT_MAJOR_VERSION_TAG_ONLY:-}
MOVE_PATCH_TAG=${INPUT_MOVE_PATCH_TAG:-}
GPG_PRIVATE_KEY="${INPUT_GPG_PRIVATE_KEY:-}"
GPG_PASSPHRASE="${INPUT_GPG_PASSPHRASE:-}"
MESSAGE="${INPUT_MESSAGE:-Release ${TAG}}"

# Check if the workflow is triggered by a tag push.
if [ "${GITHUB_REF}" = "${TAG}" ]; then
echo "This workflow is not triggered by tag push: GITHUB_REF=${GITHUB_REF}"
echo "[action-update-semver] [ERROR] This workflow is not triggered by tag push: GITHUB_REF=${GITHUB_REF}."
exit 1
fi

MESSAGE="${INPUT_MESSAGE:-Release ${TAG}}"
# Configure git and gpg if GPG key is provided.
if [ -n "${GPG_PRIVATE_KEY}" ]; then
# Import the GPG key.
echo "[action-update-semver] Importing GPG key."
echo "${GPG_PRIVATE_KEY}" | gpg --batch --yes --import

# If GPG_PASSPHRASE is set, unlock the key.
if [ -n "${GPG_PASSPHRASE}" ]; then
echo "[action-update-semver] Unlocking GPG key."
echo "${GPG_PASSPHRASE}" | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 0 --output /dev/null --sign
fi

# Set up Git.
git config user.name "${GITHUB_ACTOR}"
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
# Retrieve GPG key information.
public_key_id=$(gpg --list-secret-keys --keyid-format=long | grep sec | awk '{print $2}' | cut -d'/' -f2)
signing_key_email=$(gpg --list-keys --keyid-format=long "${public_key_id}" | grep uid | sed 's/.*<\(.*\)>.*/\1/')
signing_key_username=$(gpg --list-keys --keyid-format=long "${public_key_id}" | grep uid | sed 's/uid\s*\[\s*.*\]\s*//; s/\s*(.*//')

# Setup git user name, email, and signingkey.
echo "[action-update-semver] Setup git user name, email, and signingkey."
git config --global user.name "${signing_key_username}"
git config --global user.email "${signing_key_email}"
git config --global user.signingkey "${public_key_id}"
git config --global commit.gpgsign true
git config --global tag.gpgSign true
else
# Setup git user name and email.
echo "[action-update-semver] Setup git user name and email."
git config --global user.name "${GITHUB_ACTOR}"
git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com"
fi

# Update MAJOR/MINOR tag.
[ "${MAJOR_VERSION_TAG_ONLY}" = 'true' ] && echo_str="major version tag" || echo_str="major/minor version tags"
echo "[action-update-semver] Create ${echo_str}."
git tag -fa "${MAJOR}" -m "${MESSAGE}"
[ "${MAJOR_VERSION_TAG_ONLY}" = 'true' ] || git tag -fa "${MINOR}" -m "${MESSAGE}"
if [ ! -z "${INPUT_TAG}" ] && [ "${MOVE_PATCH_TAG}" = 'true' ]; then # Only apply when explicity
if [ -n "${INPUT_TAG}" ] && [ "${MOVE_PATCH_TAG}" = 'true' ]; then
echo "[action-update-semver] Moves ${TAG} to the latest commit."
git tag -fa "${TAG}" -m "${MESSAGE}"
fi

# Set up remote url for checkout@v1 action.
# Set up remote URL for checkout@v1 action.
if [ -n "${INPUT_GITHUB_TOKEN}" ]; then
git remote set-url origin "https://${GITHUB_ACTOR}:${INPUT_GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
fi

echo "[action-update-semver] Force push tags."
[ "${MAJOR_VERSION_TAG_ONLY}" = "true" ] || git push --force origin "${MINOR}"
if [ ! -z "${INPUT_TAG}" ] && [ "${MOVE_PATCH_TAG}" = "true" ]; then
if [ -n "${INPUT_TAG}" ] && [ "${MOVE_PATCH_TAG}" = "true" ]; then
git push --force origin "${TAG}"
fi
git push --force origin "${MAJOR}"

0 comments on commit 76674de

Please sign in to comment.