Skip to content

tests

tests #1670

Workflow file for this run

name: tests
on:
push:
branches:
- master
pull_request:
workflow_dispatch:
schedule:
- cron: '0 3 * * *'
jobs:
test:
runs-on: ubuntu-latest
steps:
# Checkout must precede setup-go so its cache can be keyed on go.sum.
- name: Checkout code
uses: actions/checkout@v7
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.23.x'
- name: Install Terraform
uses: hashicorp/setup-terraform@v4
with:
terraform_version: 'latest'
# Disable the wrapper: the acceptance-test driver must exec the real
# terraform binary, not the wrapper script shadowing it on PATH.
terraform_wrapper: false
- name: Test
env:
# Skip terraform's checkpoint version-check call in every init the tests run.
CHECKPOINT_DISABLE: "1"
run: go test -timeout 10m ./...
e2e_test:
strategy:
matrix:
terraform-version: ["0.14", "1.0", "1.8", "latest"]
config: [examples]
fail-fast: false
runs-on: ubuntu-latest
steps:
# Checkout must precede setup-go so its cache can be keyed on go.sum.
- name: Checkout code
uses: actions/checkout@v7
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.23.x'
- name: Install Terraform
uses: hashicorp/setup-terraform@v4
with:
terraform_version: ${{ matrix.terraform-version }}
- name: Add resources via Terraform
run: make terraform CONFIGURATION="${{ matrix.config }}" ARGS="apply --auto-approve --input=false"
env:
BETTERUPTIME_API_TOKEN: ${{ secrets.UPTIME_E2E_TEAM_TOKEN }}
# A data-source lookup that finds nothing yields a null output - and Terraform
# drops null outputs from state entirely, so also check every output declared
# in the configuration is actually present. Empty strings and collections
# (e.g. nobody on the default on-call calendar) hide a data problem too.
- name: Verify no output is missing, null, or empty
run: |
make terraform CONFIGURATION="${{ matrix.config }}" ARGS="output -json > outputs.json"
echo "Terraform outputs:"
jq 'map_values(.value)' "${{ matrix.config }}/outputs.json"
DECLARED="$(grep -hoE '^output "[^"]+"' "${{ matrix.config }}"/*.tf | sed -E 's/^output "([^"]+)"/\1/' | sort -u)"
MISSING="$(comm -23 <(echo "$DECLARED") <(jq -r 'keys[]' "${{ matrix.config }}/outputs.json" | sort -u))"
EMPTY="$(jq -r 'to_entries[] | select(.value.value == null or .value.value == "" or .value.value == [] or .value.value == {}) | .key' "${{ matrix.config }}/outputs.json")"
if [ -n "$MISSING" ] || [ -n "$EMPTY" ]; then
if [ -n "$MISSING" ]; then
echo "Declared outputs missing a value:"
echo "$MISSING"
fi
if [ -n "$EMPTY" ]; then
echo "Outputs with null or empty values:"
echo "$EMPTY"
fi
exit 1
fi
echo "All $(echo "$DECLARED" | wc -l) declared outputs have values."
env:
BETTERUPTIME_API_TOKEN: ${{ secrets.UPTIME_E2E_TEAM_TOKEN }}
- name: Plan resources via Terraform - must be empty
run: |
make terraform CONFIGURATION="${{ matrix.config }}" ARGS="plan --input=false --out=tfplan"
make terraform CONFIGURATION="${{ matrix.config }}" ARGS="show --json tfplan > tfplan.json"
CHANGES="$(jq "[.resource_changes[]? | select(.change.actions != [\"no-op\"])] | length" "${{ matrix.config }}/tfplan.json")"
if [ "$CHANGES" == "0" ]; then
echo "No planned changes detected after first apply. Success!"
else
echo "$CHANGES planned changes detected after first apply. Failure!"
exit 1
fi
env:
BETTERUPTIME_API_TOKEN: ${{ secrets.UPTIME_E2E_TEAM_TOKEN }}
- name: Destroy resources via Terraform
if: always()
run: make terraform CONFIGURATION="${{ matrix.config }}" ARGS="destroy --auto-approve --input=false"
env:
BETTERUPTIME_API_TOKEN: ${{ secrets.UPTIME_E2E_TEAM_TOKEN }}
# Assembles every documented example into one configuration - the basic example's
# scaffolding (provider/versions/variables/random) without its own main.tf/outputs.tf,
# plus every resources/* and data-sources/* example - and runs it as a single
# apply/plan/destroy, keeping the registry docs' snippets verified.
e2e_combined:
strategy:
matrix:
terraform-version: ["0.14", "1.0", "1.8", "latest"]
fail-fast: false
runs-on: ubuntu-latest
steps:
# Checkout must precede setup-go so its cache can be keyed on go.sum.
- name: Checkout code
uses: actions/checkout@v7
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.23.x'
- name: Install Terraform
uses: hashicorp/setup-terraform@v4
with:
terraform_version: ${{ matrix.terraform-version }}
- name: Assemble combined examples
run: |
mkdir -p combined-e2e
# Reuse the basic example's scaffolding (provider/versions/variables/random),
# but not its own resources/outputs.
for f in examples/*.tf; do
case "$(basename "$f")" in main.tf | outputs.tf) continue ;; esac
cp "$f" "combined-e2e/$(basename "$f")"
done
# Every resource and data-source example runs in the combined config.
# Data sources look up pre-existing "My Existing ..." fixtures on the team.
for f in examples/resources/*/*.tf; do
dir="$(basename "$(dirname "$f")")"
cp "$f" "combined-e2e/res__${dir}__$(basename "$f")"
done
for f in examples/data-sources/*/*.tf; do
dir="$(basename "$(dirname "$f")")"
cp "$f" "combined-e2e/ds__${dir}__$(basename "$f")"
done
# Invitation e-mails must be unique across the concurrent matrix jobs; the
# docs keep clean addresses, the combined run suffixes them with the pet.
sed -i -E 's/(alice|bob|dylan)@betterstack\.com/\1+${random_pet.unique.id}@betterstack.com/' combined-e2e/res__betteruptime_team_member__resource.tf
- name: Add resources via Terraform
run: make terraform CONFIGURATION="combined-e2e" ARGS="apply --auto-approve --input=false"
env:
BETTERUPTIME_API_TOKEN: ${{ secrets.UPTIME_E2E_TEAM_TOKEN }}
# A data-source lookup that finds nothing yields a null output - and Terraform
# drops null outputs from state entirely, so also check every output declared
# in the configuration is actually present. Empty strings and collections
# (e.g. nobody on the default on-call calendar) hide a data problem too.
- name: Verify no output is missing, null, or empty
run: |
make terraform CONFIGURATION="combined-e2e" ARGS="output -json > outputs.json"
echo "Terraform outputs:"
jq 'map_values(.value)' "combined-e2e/outputs.json"
DECLARED="$(grep -hoE '^output "[^"]+"' combined-e2e/*.tf | sed -E 's/^output "([^"]+)"/\1/' | sort -u)"
MISSING="$(comm -23 <(echo "$DECLARED") <(jq -r 'keys[]' "combined-e2e/outputs.json" | sort -u))"
EMPTY="$(jq -r 'to_entries[] | select(.value.value == null or .value.value == "" or .value.value == [] or .value.value == {}) | .key' "combined-e2e/outputs.json")"
if [ -n "$MISSING" ] || [ -n "$EMPTY" ]; then
if [ -n "$MISSING" ]; then
echo "Declared outputs missing a value:"
echo "$MISSING"
fi
if [ -n "$EMPTY" ]; then
echo "Outputs with null or empty values:"
echo "$EMPTY"
fi
exit 1
fi
echo "All $(echo "$DECLARED" | wc -l) declared outputs have values."
env:
BETTERUPTIME_API_TOKEN: ${{ secrets.UPTIME_E2E_TEAM_TOKEN }}
- name: Plan resources via Terraform - must be empty
run: |
make terraform CONFIGURATION="combined-e2e" ARGS="plan --input=false --out=tfplan"
make terraform CONFIGURATION="combined-e2e" ARGS="show --json tfplan > tfplan.json"
CHANGES="$(jq "[.resource_changes[]? | select(.change.actions != [\"no-op\"])] | length" "combined-e2e/tfplan.json")"
if [ "$CHANGES" == "0" ]; then
echo "No planned changes detected after first apply. Success!"
else
echo "$CHANGES planned changes detected after first apply. Failure!"
exit 1
fi
env:
BETTERUPTIME_API_TOKEN: ${{ secrets.UPTIME_E2E_TEAM_TOKEN }}
- name: Destroy resources via Terraform
if: always()
run: make terraform CONFIGURATION="combined-e2e" ARGS="destroy --auto-approve --input=false"
env:
BETTERUPTIME_API_TOKEN: ${{ secrets.UPTIME_E2E_TEAM_TOKEN }}
notify-linear:
if: ${{ failure() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event_name == 'schedule') }}
needs: [e2e_test, e2e_combined]
runs-on: ubuntu-latest
permissions:
contents: read
actions: read
steps:
- name: "Gather failure context"
id: ctx
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
FAILED=$(gh api \
"repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}/jobs" \
--paginate \
--jq '[.jobs[]
| select(.conclusion == "failure")
| "- **\(.name)** — failed at _\([.steps[] | select(.conclusion == "failure") | .name] | join(", "))_"]
| join("\n")')
if [ -z "$FAILED" ]; then
FAILED="_(no failed jobs reported by the API yet)_"
fi
{
echo "failed_jobs<<CTXEOF"
echo "$FAILED"
echo "CTXEOF"
} >> "$GITHUB_OUTPUT"
- name: "Create Linear issue"
env:
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
LINEAR_TEAM_ID: ${{ secrets.LINEAR_TEAM_ID }}
LINEAR_STATUS_ID: ${{ secrets.LINEAR_STATUS_PLAN_ID }}
LINEAR_PROJECT_ID: ${{ secrets.LINEAR_PROJECT_ID }}
LINEAR_ASSIGNEE_ID: ${{ secrets.LINEAR_ASSIGNEE_ID }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
EVENT_NAME: ${{ github.event_name }}
SHA: ${{ github.sha }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
FAILED_JOBS: ${{ steps.ctx.outputs.failed_jobs }}
run: |
SHORT_SHA="${SHA:0:7}"
# head_commit is only populated on push events; skip the line otherwise
COMMIT_LINE="**Commit:** \`$SHORT_SHA\`"
if [ -n "$COMMIT_MESSAGE" ]; then
COMMIT_SUBJECT="$(printf '%s' "$COMMIT_MESSAGE" | head -n1)"
COMMIT_LINE="**Commit:** \`$SHORT_SHA\` — $COMMIT_SUBJECT"
fi
ATTEMPT_NOTE=""
if [ "$RUN_ATTEMPT" != "1" ]; then
ATTEMPT_NOTE=" (attempt #$RUN_ATTEMPT)"
fi
TITLE="E2E failure: ${{ github.workflow }} (${{ github.repository }})"
DESCRIPTION=$(cat <<EOF
**${{ github.workflow }}** failed on \`${{ github.ref_name }}\` in \`${{ github.repository }}\` via \`$EVENT_NAME\`$ATTEMPT_NOTE
**Failing jobs:**
$FAILED_JOBS
$COMMIT_LINE
[View failing run]($RUN_URL)
EOF
)
PAYLOAD=$(jq -n \
--arg query 'mutation IssueCreate($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { identifier url } } }' \
--arg title "$TITLE" \
--arg description "$DESCRIPTION" \
--arg teamId "$LINEAR_TEAM_ID" \
--arg assigneeId "$LINEAR_ASSIGNEE_ID" \
--arg stateId "$LINEAR_STATUS_ID" \
--arg projectId "$LINEAR_PROJECT_ID" \
'{
query: $query,
variables: {
input: {
title: $title,
description: $description,
teamId: $teamId,
assigneeId: $assigneeId,
stateId: $stateId,
projectId: $projectId,
priority: 2
}
}
}')
curl -sSf -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H 'Content-Type: application/json' \
-d "$PAYLOAD"