Skip to content
172 changes: 172 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
name: Release

on:
workflow_dispatch:

permissions:
contents: read

jobs:
create-release:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
outputs:
tag: ${{ github.ref_name }}
steps:
- name: Create draft release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.ref_name }}
run: |
gh release create "$TAG" \
--repo "$GITHUB_REPOSITORY" \
--draft \
--title "$TAG"

build:
needs: create-release
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
runner: codebuild-aws-lambda-cpp-test-trigger-x86-${{ github.run_id }}-${{ github.run_attempt }}
- arch: aarch64
runner: codebuild-aws-lambda-cpp-test-trigger-arm64-${{ github.run_id }}-${{ github.run_attempt }}

steps:
- name: Install prerequisites
env:
AWS_DEFAULT_REGION: eu-west-1
CA_DOMAIN: aws-lambda
CA_DOMAIN_OWNER: ${{ secrets.AWS_ACCOUNT_ID }}
CA_REPO: pypi-store
run: |
aws codeartifact login --tool pip \
--domain "$CA_DOMAIN" --domain-owner "$CA_DOMAIN_OWNER" \
--repository "$CA_REPO"
pip install cmake ninja
cmake --version
ninja --version

- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.ref_name }}

- name: Build in Release mode
run: |
cmake -B build -GNinja \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_TESTS=ON
cmake --build build

- name: Run unit tests
run: cd build && ctest --output-on-failure

- name: Extract library
run: |
mkdir -p staging
cp build/libaws-lambda-runtime.a staging/libaws-lambda-runtime-${{ matrix.arch }}.a

- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: libaws-lambda-runtime-${{ matrix.arch }}
path: staging/libaws-lambda-runtime-${{ matrix.arch }}.a

upload:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
# The signing role's trust policy is scoped to the `environment:release`
# OIDC subject claim. Without this, the token carries a `ref:` subject and
# AssumeRoleWithWebIdentity is denied. See docs/gpg-key-management.md.
environment: release
permissions:
id-token: write
contents: write

steps:
- name: Download all artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: artifacts
merge-multiple: true

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@517a711dbcd0e402f90c77e7e2f81e849156e31d # v6.2.2
with:
role-to-assume: ${{ secrets.AWS_GPG_SIGNING_ROLE_ARN }}
aws-region: ${{ secrets.AWS_REGION }}

- name: Import GPG key from Secrets Manager
env:
GPG_SECRET_ID: lambda-runtimes/cpp/gpg-signing-key
run: |
# Isolate the keyring to the runner's workspace so nothing leaks into
# the default ~/.gnupg, and keep the key material off disk entirely:
# Secrets Manager streams it straight into gpg --import via stdin.
GNUPGHOME="$(mktemp -d)"
chmod 700 "$GNUPGHOME"
echo "GNUPGHOME=$GNUPGHOME" >> "$GITHUB_ENV"

aws secretsmanager get-secret-value \
--secret-id "$GPG_SECRET_ID" \
--query SecretString \
--output text \
| gpg --batch --homedir "$GNUPGHOME" --import

# Resolve the fingerprint rather than relying on gpg's default key
# selection, so signing fails loudly if the import went wrong.
FPR="$(gpg --homedir "$GNUPGHOME" --with-colons --list-secret-keys \
| awk -F: '/^fpr:/ {print $10; exit}')"
if [ -z "$FPR" ]; then
echo "No secret key imported from $GPG_SECRET_ID" >&2
exit 1
fi
echo "GPG_FINGERPRINT=$FPR" >> "$GITHUB_ENV"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like you are passing the GPG_FINGERPRINT to the generic $GITHUB_ENV you should take the GPG key sign and discard.

If one gets access to the workflow because of the vulnerability at a later action they get the GPG key.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put everything in the same step so we don't need to pass anything between steps

echo "Imported signing key $FPR"

- name: Sign artifacts and generate checksums
run: |
cd artifacts

# Sign each artifact
for file in *.a; do
gpg --batch --yes --local-user "$GPG_FINGERPRINT" \
--armor --detach-sign "$file"
done

# Generate checksums file and sign it
sha256sum ./*.a > SHA256SUMS
gpg --batch --yes --local-user "$GPG_FINGERPRINT" \
--armor --detach-sign SHA256SUMS

- name: Verify signatures
run: |
cd artifacts
gpg --batch --verify SHA256SUMS.asc SHA256SUMS
for file in *.a; do
gpg --batch --verify "$file.asc" "$file"
done

- name: Upload assets to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.ref_name }}
run: |
gh release upload "$TAG" \
--repo "$GITHUB_REPOSITORY" \
artifacts/*

- name: Destroy imported key material
if: always()
run: |
if [ -n "${GNUPGHOME:-}" ] && [ -d "$GNUPGHOME" ]; then
gpgconf --homedir "$GNUPGHOME" --kill all || true
rm -rf "$GNUPGHOME"
fi
Loading