Merge pull request #52 from dev-dull/ci #133
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build and Run Docker Image | |
on: | |
schedule: | |
# Refresh monthly to pick up base image updates | |
- cron: '0 0 1 * *' | |
push: | |
branches: | |
- main | |
jobs: | |
build: | |
runs-on: ubuntu-latest | |
outputs: | |
DOCKER_IMAGE_NAME: ${{ steps.build-image-name.outputs.DOCKER_IMAGE_NAME }} | |
VERSION: ${{ steps.build-image-name.outputs.VERSION }} | |
CODELINE: ${{ steps.build-image-name.outputs.CODELINE }} | |
INSTANCE_NAME: ${{ steps.build-image-name.outputs.INSTANCE_NAME }} | |
steps: | |
- name: Checkout Code | |
uses: actions/checkout@v4 | |
- name: Build Image Name | |
id: build-image-name | |
# TODO: going to both ENV and OUTPUTS is probably redundant | |
run: | | |
version=$(cat pyterrabacktyl.py | grep __version__ | tr -d "[a-zA-Z ='_]") | |
# Seems like GITHUB_ENV value can be changed, but value in GITHUB_OUTPUT can NOT | |
if [ github.ref_name != 'main' ]; then | |
echo "CODELINE=${{ github.ref_name }}_${version}" | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT | |
else | |
echo CODELINE=$(echo ${{ github.ref_name }} | tr '[A-Z]' '[a-z]') | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT | |
fi | |
echo DOCKER_IMAGE_NAME=devdull/$(echo ${{ github.event.repository.name }} | tr '[A-Z]' '[a-z]') | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT | |
echo INSTANCE_NAME=${{ github.ref_name }}_InT | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT | |
echo "VERSION=${version}" | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT | |
- name: Build Docker Image | |
run: | | |
docker build . --file Dockerfile --tag ${{ env.DOCKER_IMAGE_NAME }}:${{ env.CODELINE }} | |
echo '## Image Details' >> $GITHUB_STEP_SUMMARY | |
header=$(docker images | sed -r 's/\s{2,}/|/g' | grep -E '^R' | sed -r 's/^|$/\|/g') | |
echo "$header" >> $GITHUB_STEP_SUMMARY | |
echo "$header" | sed -r 's/[^|]/-/g' >> $GITHUB_STEP_SUMMARY | |
docker images | sed -r 's/\s{2,}/|/g' | grep -E 'pyterrabacktyl' | sed -r 's/^|$/\|/g' >> $GITHUB_STEP_SUMMARY | |
docker save ${{ env.DOCKER_IMAGE_NAME }}:${{ env.CODELINE }} -o /tmp/${{ github.event.repository.name }}.tar | |
- name: Upload Docker Image | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ github.event.repository.name }}.tar | |
path: /tmp/${{ github.event.repository.name }}.tar | |
- name: Create summary header | |
run: | | |
echo '## Test Results' | tee -a $GITHUB_STEP_SUMMARY | |
run_and_test: | |
runs-on: ubuntu-latest | |
needs: build | |
strategy: | |
matrix: | |
# TODO: Test OpenTofu | |
terraform_version: | |
- '1.10.4' # Latest | |
- '1.5.6' # Popular | |
- '1.3.9' # Popular | |
steps: | |
- name: Checkout Code | |
uses: actions/checkout@v4 | |
- name: Download Docker Image | |
uses: actions/download-artifact@v4 | |
with: | |
name: ${{ github.event.repository.name }}.tar | |
path: /tmp | |
- name: Load Docker Image | |
run: | | |
docker load -i /tmp/${{ github.event.repository.name }}.tar | |
- name: Run Docker Container | |
run: | | |
docker run -d --name ${{ needs.build.outputs.INSTANCE_NAME }} -p 2442:2442 ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} | |
- name: Wait for PyTerraBackTYL to start | |
run: | | |
for ct in {0..9} | |
do | |
# '000' gets set if curl fails | |
STATUS=$(curl -Ss -o /dev/null -w "%{http_code}" http://localhost:2442 || true) | |
if [ ${STATUS} -ne 200 ]; then | |
echo "waiting..." | |
sleep 1 | |
else | |
exit 0 | |
fi | |
done | |
exit 1 | |
- name: Set up Terraform | |
uses: hashicorp/setup-terraform@v3 | |
with: | |
terraform_version: ${{ matrix.terraform_version }} | |
# TODO: nicely formatted summary output doesn't show the failures | |
# Maybe just write a bunch of shell scripts and exec them here | |
# Loop over them and report the name ($0), results (✅, ❌) | |
- name: Set headers for test output | |
run: | | |
echo '## Terraform ${{ matrix.terraform_version }}' >> $GITHUB_STEP_SUMMARY | |
COLS="|Test Name|Status|" | |
echo "${COLS}" >> /tmp/summary | |
echo "${COLS}" | sed -r 's/[^|]/-/g' >> /tmp/summary | |
- name: Run 'happy-path' Test | |
run: | | |
cd .github/build_tests | |
terraform init | |
terraform plan | |
terraform apply --auto-approve | |
docker logs ${{ needs.build.outputs.INSTANCE_NAME }} | |
echo TFSTATE=$(curl -sS http://localhost:2442/?env=InT) | tee -a $GITHUB_ENV | |
echo "|Happy Path|✅ Success|" >> /tmp/summary | |
- name: Validate Terraform State | |
# Validate that Terraform state saved in PyTerraBackTYL matches the content of the file generated by Terraform | |
run: | | |
cd .github/build_tests | |
TF_CONTENT=$(echo '${{ env.TFSTATE }}' | jq -r '.resources[].instances[].attributes.content | select(. != null)') | |
FILE_CONTENT=$(cat test.txt) | |
if [[ "${TF_CONTENT}" != "${FILE_CONTENT}" ]]; then | |
echo ${TF_CONTENT} != ${FILE_CONTENT} >&2 | |
exit 1 | |
fi | |
echo -e "|State Saved|✅ Success|" >> /tmp/summary | |
- name: Validate Terraform State Changed | |
# The ID of the null resource should change with every apply. Verify PyTerraBackTYL is saving the updated state | |
run: | | |
cd .github/build_tests | |
CURRENT_NULL_RESOURCE_ID=$(echo '${{env.TFSTATE}}' | jq -r '.resources[] | select(.type == "null_resource") | .instances[].attributes.id') | |
terraform apply --auto-approve | |
NEW_NULL_RESOURCE_ID=$(curl -sS http://localhost:2442/?env=InT | jq -r '.resources[] | select(.type == "null_resource") | .instances[].attributes.id') | |
[ -n "${NEW_NULL_RESOURCE_ID}" ] | |
[ -n "${CURRENT_NULL_RESOURCE_ID}" ] | |
[ "${CURRENT_NULL_RESOURCE_ID}" -ne "${NEW_NULL_RESOURCE_ID}" ] | |
echo -e "|State Changed|✅ Success|" >> /tmp/summary | |
- name: Run Terraform Test, Locked ENV | |
# Manually lock the environment and capture how terraform exited | |
id: prelock_InT | |
continue-on-error: true | |
run: | | |
curl -X LOCK -sS http://localhost:2442/lock?env=InT | |
cd .github/build_tests | |
terraform apply --auto-approve | |
- name: Verify Command Failed Successfully | |
# Verify that terraform failed with a lock error when trying to run against a locked environment | |
run: | | |
if [ "${{ steps.prelock_InT.outcome }}" != "success" ]; then | |
curl -X UNLOCK -sS http://localhost:2442/unlock?env=InT | |
echo "|Locked Environment is Blocking|✅ Success|" >> /tmp/summary | |
else | |
# Picked confusing wording just to be silly | |
echo "Step unexpectedly failed to fail as expected." | |
exit 1 | |
fi | |
- name: Show results, Clean Up | |
run: | | |
cat /tmp/summary | tee -a $GITHUB_STEP_SUMMARY | |
# Hopefully this is being nice to GHA infra and not just wasting CPU cycles | |
docker rm -f ${{ needs.build.outputs.INSTANCE_NAME }} || true | |
docker rmi ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} || true | |
tag_and_push: | |
runs-on: ubuntu-latest | |
needs: | |
- build | |
- run_and_test | |
steps: | |
- name: Download Docker Image | |
uses: actions/download-artifact@v4 | |
with: | |
name: ${{ github.event.repository.name }}.tar | |
path: /tmp | |
- name: Load Docker Image | |
run: | | |
docker load -i /tmp/${{ github.event.repository.name }}.tar | |
- name: Docker Login | |
run: | | |
echo '${{ secrets.DOCKERHUB_PASSWORD }}' | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin | |
echo '## Push image to DockerHub (v${{ needs.build.outputs.VERSION }})' >> $GITHUB_STEP_SUMMARY | |
- name: Tag Docker Image | |
if: github.ref_name != 'main' | |
run: | | |
docker push ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} | |
echo "${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }}" >> $GITHUB_STEP_SUMMARY | |
- name: Tag and Push Latest | |
if: github.ref_name == 'main' | |
run: | | |
docker tag ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:latest | |
docker tag ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.VERSION }} | |
docker push ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:latest | |
echo "${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }}" >> $GITHUB_STEP_SUMMARY |