Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
836bfae
Add Docker scaffolding
mcdonnnj May 20, 2022
75e637e
Add the script to build a deployment package artifact
mcdonnnj May 20, 2022
d824f15
Add Pipfiles for each supported Python version
mcdonnnj May 20, 2022
109d223
Add Lambda handler
mcdonnnj May 23, 2022
7c5ea91
Update the Lambda build process
mcdonnnj May 24, 2022
87eae5e
Add a `build` job to the GitHub Actions workflow
mcdonnnj May 24, 2022
0b8c693
Update project README
mcdonnnj May 25, 2022
02afeb5
Add version management
mcdonnnj May 25, 2022
7f3aa84
Add a barebones testing harness
mcdonnnj May 25, 2022
9c50c1f
Add a CodeQL workflow
mcdonnnj May 25, 2022
a2a4383
Add a release workflow
mcdonnnj May 25, 2022
80f0872
Fix vendor label in Dockerfile
mcdonnnj May 25, 2022
a4a5cde
Update to use long flag names
mcdonnnj May 25, 2022
b2014b6
Improve error messages for the `divide` task
mcdonnnj May 25, 2022
56b9ac9
Fix language in `lambda_handler.py`
mcdonnnj May 25, 2022
3d9c3dd
Update the bandit pre-commit hook entries
mcdonnnj May 27, 2022
9014297
Update `pytest.ini` to use long flag names
mcdonnnj May 27, 2022
6cb7864
Update Dockerfile label information
mcdonnnj Nov 29, 2022
1468097
Update the artifact production process
mcdonnnj Nov 30, 2022
95d619a
Update dependency installation
mcdonnnj Nov 30, 2022
7d5c25e
Add Python dependency maintenance instructions
mcdonnnj Nov 30, 2022
2b5f9d7
Update Python dependencies
mcdonnnj Nov 30, 2022
915eb71
Update missed GitHub Action versions
mcdonnnj Nov 30, 2022
6f98ec5
Add commented out dependabot ignore information
mcdonnnj Nov 30, 2022
78b1cd2
Remove the release.yml workflow
mcdonnnj Nov 30, 2022
bde97e0
Pull in updates to the bump_version.sh script
mcdonnnj Nov 30, 2022
2a44cdc
Use a multi-stage build for the Lambda image
mcdonnnj Dec 1, 2022
a62a300
Ensure pipenv uses the system Python instead of a venv
mcdonnnj Dec 1, 2022
a0036c7
Enable changing the image tag used with Docker Compose
mcdonnnj Dec 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bandit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ tests:
# - B102

skips:
# - B101 # skip "assert used" check since assertions are required in pytests
- B101 # skip "assert used" check since assertions are required in pytests
2 changes: 2 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ updates:
- dependency-name: actions/setup-python
- dependency-name: hashicorp/setup-terraform
- dependency-name: mxschmitt/action-tmate
# Managed by cisagov/skeleton-aws-lambda-python
# - dependency-name: actions/upload-artifact

- package-ecosystem: "pip"
directory: "/"
Expand Down
69 changes: 69 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:

env:
CURL_CACHE_DIR: ~/.cache/curl
DEFAULT_ARTIFACT_NAME: lambda_build.zip
PIP_CACHE_DIR: ~/.cache/pip
PRE_COMMIT_CACHE_DIR: ~/.cache/pre-commit
RUN_TMATE: ${{ secrets.RUN_TMATE }}
Expand Down Expand Up @@ -98,3 +99,71 @@ jobs:
- name: Setup tmate debug session
uses: mxschmitt/action-tmate@v3
if: env.RUN_TMATE
test:
runs-on: ubuntu-latest
needs: lint
steps:
- id: setup-env
uses: cisagov/setup-env-github-action@develop
- uses: actions/checkout@v3
- id: setup-python
uses: actions/setup-python@v4
with:
python-version: "3.10"
- uses: actions/cache@v3
env:
BASE_CACHE_KEY: "${{ github.job }}-${{ runner.os }}-\
py${{ steps.setup-python.outputs.python-version }}-"
with:
path: |
${{ env.PIP_CACHE_DIR }}
key: "${{ env.BASE_CACHE_KEY }}\
${{ hashFiles('**/requirements-test.txt') }}-\
${{ hashFiles('**/requirements.txt') }}"
restore-keys: |
${{ env.BASE_CACHE_KEY }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade --requirement requirements-test.txt
- name: Run tests
env:
GITHUB_RELEASE_TAG: ${{ github.event.release.tag_name }}
run: pytest
- name: Setup tmate debug session
uses: mxschmitt/action-tmate@v3
if: env.RUN_TMATE
build:
runs-on: ubuntu-latest
needs:
- lint
- test
strategy:
matrix:
# Python runtime versions supported by AWS
python-version:
- "3.7"
- "3.8"
- "3.9"
steps:
- uses: actions/checkout@v3
- name: Get the short SHA for the commit being used
run: |
echo "GH_SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV
- name: Build the base Lambda Docker image
run: |
docker compose build \
--build-arg PY_VERSION=${{ matrix.python-version }} \
build_deployment_package
- name: Generate the Lambda deployment package
run: docker compose up build_deployment_package
- name: Upload the generated Lambda deployment package as an artifact
uses: actions/upload-artifact@v3
with:
name: "${{ github.event.repository.name }}-\
py${{ matrix.python-version }}-\
${{ env.GH_SHORT_SHA }}"
path: "${{ env.DEFAULT_ARTIFACT_NAME }}"
- name: Setup tmate debug session
uses: mxschmitt/action-tmate@v3
if: env.RUN_TMATE
67 changes: 67 additions & 0 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"

on:
push:
# Dependabot triggered push events have read-only access, but uploading code
# scanning requires write access.
branches-ignore:
- dependabot/**
pull_request:
# The branches below must be a subset of the branches above
branches:
- develop
schedule:
- cron: '0 14 * * 6'

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
# required for all workflows
security-events: write
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are go, javascript, csharp, python, cpp, and java
language:
- python
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection

steps:
- name: Checkout repository
uses: actions/checkout@v3

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}

# Autobuild attempts to build any compiled languages (C/C++, C#, or
# Java). If this step fails, then you should remove it and run the build
# manually (see below).
- name: Autobuild
uses: github/codeql-action/autobuild@v2

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

# ✏️ If the Autobuild fails above, remove it and uncomment the following
# three lines and modify them (or add more) to build your code if your
# project uses a compiled language

# - run: |
# make bootstrap
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
# Files already tracked by Git are not affected.
# See: https://git-scm.com/docs/gitignore

## Project Specific ##
lambda_build.zip

## Python ##
__pycache__
.mypy_cache
Expand Down
2 changes: 2 additions & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ import_heading_stdlib=Standard Python Libraries
import_heading_thirdparty=Third-Party Libraries
import_heading_firstparty=cisagov Libraries

known_first_party=example

# Run isort under the black profile to align with our other Python linting
profile=black
10 changes: 10 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,22 @@ repos:
- id: shell-lint

# Python hooks
# Run bandit on the tests directory with a custom configuration
- repo: https://github.com/PyCQA/bandit
rev: 1.7.4
hooks:
- id: bandit
name: bandit (tests directory)
files: tests
args:
- --config=.bandit.yml
# Run bandit on everything but the tests directory
- repo: https://github.com/PyCQA/bandit
rev: 1.7.4
hooks:
- id: bandit
name: bandit (everything but the tests directory)
exclude: tests
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
Expand Down
57 changes: 57 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
ARG PY_VERSION=3.9

FROM amazon/aws-lambda-python:$PY_VERSION as install-stage

# Declare it a second time so it's brought into this scope.
ARG PY_VERSION=3.9

# Install the Python packages necessary to install the Lambda dependencies.
RUN python3 -m pip install --no-cache-dir \
pip \
setuptools \
wheel \
# This version of pipenv is the minimum version to allow passing arguments
# to pip with the --extra-pip-args option.
&& python3 -m pip install --no-cache-dir "pipenv>=2022.9.8"

WORKDIR /tmp

# Copy in the dependency files.
COPY src/py$PY_VERSION/ .

# Install the Lambda dependencies.
#
# The --extra-pip-args option is used to pass necessary arguments to the
# underlying pip calls.
RUN pipenv sync --system --extra-pip-args="--no-cache-dir --target ${LAMBDA_TASK_ROOT}"

FROM amazon/aws-lambda-python:$PY_VERSION as build-stage

###
# For a list of pre-defined annotation keys and value types see:
# https://github.com/opencontainers/image-spec/blob/master/annotations.md
###
# github@cisa.dhs.gov is a very generic email distribution, and it is
# unlikely that anyone on that distribution is familiar with the
# particulars of your repository. It is therefore *strongly*
# suggested that you use an email address here that is specific to the
# person or group that maintains this repository; for example:
# LABEL org.opencontainers.image.authors="vm-fusion-dev-group@trio.dhs.gov"
LABEL org.opencontainers.image.authors="github@cisa.dhs.gov"
LABEL org.opencontainers.image.vendor="Cybersecurity and Infrastructure Security Agency"

# Declare it a third time so it's brought into this scope.
ARG PY_VERSION=3.9

# This must be present in the image to generate a deployment artifact.
ENV BUILD_PY_VERSION=$PY_VERSION

COPY --from=install-stage ${LAMBDA_TASK_ROOT} ${LAMBDA_TASK_ROOT}

WORKDIR ${LAMBDA_TASK_ROOT}

# Copy in the handler.
COPY src/lambda_handler.py .

# Ensure our handler is invoked when the image is used.
CMD ["lambda_handler.handler"]
68 changes: 64 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,74 @@
[![GitHub Build Status](https://github.com/cisagov/skeleton-aws-lambda-python/workflows/build/badge.svg)](https://github.com/cisagov/skeleton-aws-lambda-python/actions)

This is a generic skeleton project that can be used to quickly get a
new [cisagov](https://github.com/cisagov) GitHub project started.
This skeleton project contains [licensing information](LICENSE), as
new [cisagov](https://github.com/cisagov) GitHub
[AWS Lambda](https://aws.amazon.com/lambda/) project using the Python runtimes
started. This skeleton project contains [licensing information](LICENSE), as
well as [pre-commit hooks](https://pre-commit.com) and
[GitHub Actions](https://github.com/features/actions) configurations
appropriate for the major languages that we use.

In many cases you will instead want to use one of the more specific
skeleton projects derived from this one.
## Building the base Lambda image ##

The base Lambda image can be built with the following command:

```console
docker compose build
```

This base image is used both to build a deployment package and to run the
Lambda locally.

## Building a deployment package ##

You can build a deployment zip file to use when creating a new AWS Lambda
function with the following command:

```console
docker compose up build_deployment_package
```

This will output the deployment zip file in the root directory.

## Running the Lambda locally ##

The configuration in this repository allows you run the Lambda locally for
testing as long as you do not need explicit permissions for other AWS
services. This can be done with the following command:

```console
docker compose up --detach run_lambda_locally
```

You can then invoke the Lambda using the following:

```console
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
```

The `{}` in the command is the invocation event payload to send to the Lambda
and would be the value given as the `event` argument to the handler.

Once you are finished you can stop the detached container with the following command:

```console
docker compose down
```

## How to update Python dependencies ##

The Python dependencies are maintained using a [Pipenv](https://github.com/pypa/pipenv)
configuration for each supported Python version. Changes to requirements
should be made to the respective `src/py<Python version>/Pipfile`. More
information about the `Pipfile` format can be found [here](https://pipenv.pypa.io/en/latest/basics/#example-pipfile-pipfile-lock).
The accompanying `Pipfile.lock` files contain the specific dependency versions
that will be installed. These files can be updated like so (using the Python
3.9 configuration as an example):

```console
cd src/py3.9
pipenv lock
```

## New Repositories from a Skeleton ##

Expand Down
53 changes: 53 additions & 0 deletions bump_version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash

# bump_version.sh (show|major|minor|patch|prerelease|build)

set -o nounset
set -o errexit
set -o pipefail

VERSION_FILE=src/version.txt

HELP_INFORMATION="bump_version.sh (show|major|minor|patch|prerelease|build|finalize)"

old_version=$(sed -n "s/^__version__ = \"\(.*\)\"$/\1/p" $VERSION_FILE)
# Comment out periods so they are interpreted as periods and don't
# just match any character
old_version_regex=${old_version//\./\\\.}

if [ $# -ne 1 ]; then
echo "$HELP_INFORMATION"
else
case $1 in
major | minor | patch | prerelease | build)
new_version=$(python -c "import semver; print(semver.bump_$1('$old_version'))")
echo Changing version from "$old_version" to "$new_version"
# A temp file is used to provide compatability with macOS development
# as a result of macOS using the BSD version of sed
tmp_file=/tmp/version.$$
sed "s/$old_version_regex/$new_version/" $VERSION_FILE > $tmp_file
mv $tmp_file $VERSION_FILE
git add $VERSION_FILE
git commit -m"Bump version from $old_version to $new_version"
git push
;;
finalize)
new_version=$(python -c "import semver; print(semver.finalize_version('$old_version'))")
echo Changing version from "$old_version" to "$new_version"
# A temp file is used to provide compatability with macOS development
# as a result of macOS using the BSD version of sed
tmp_file=/tmp/version.$$
sed "s/$old_version_regex/$new_version/" $VERSION_FILE > $tmp_file
mv $tmp_file $VERSION_FILE
git add $VERSION_FILE
git commit -m"Finalize version from $old_version to $new_version"
git push
;;
show)
echo "$old_version"
;;
*)
echo "$HELP_INFORMATION"
;;
esac
fi
Loading