chore: add prettier/typo check to CI (#14)

## Changes made
- Added back CI steps for validating the codebase for typos and formatting
- Updated README validation CI step to be dependent on typo-checking step
- Updated configuration files as needed to support the new CI step
- Updated all files that were previously getting skipped over from improperly-set-up CI logic
This commit is contained in:
Michael Smith 2025-04-29 10:09:22 -04:00 committed by GitHub
parent 0ce1e7ab01
commit 9e18a4e3a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 200 additions and 169 deletions

View File

@ -4,23 +4,23 @@ set -u
VERBOSE="${VERBOSE:-0}" VERBOSE="${VERBOSE:-0}"
if [[ "${VERBOSE}" -ne "0" ]]; then if [[ "${VERBOSE}" -ne "0" ]]; then
set -x set -x
fi fi
# List of required environment variables # List of required environment variables
required_vars=( required_vars=(
"INSTATUS_API_KEY" "INSTATUS_API_KEY"
"INSTATUS_PAGE_ID" "INSTATUS_PAGE_ID"
"INSTATUS_COMPONENT_ID" "INSTATUS_COMPONENT_ID"
"VERCEL_API_KEY" "VERCEL_API_KEY"
) )
# Check if each required variable is set # Check if each required variable is set
for var in "${required_vars[@]}"; do for var in "${required_vars[@]}"; do
if [[ -z "${!var:-}" ]]; then if [[ -z "${!var:-}" ]]; then
echo "Error: Environment variable '$var' is not set." echo "Error: Environment variable '$var' is not set."
exit 1 exit 1
fi fi
done done
REGISTRY_BASE_URL="${REGISTRY_BASE_URL:-https://registry.coder.com}" REGISTRY_BASE_URL="${REGISTRY_BASE_URL:-https://registry.coder.com}"
@ -31,38 +31,38 @@ declare -a failures=()
# Collect all module directories containing a main.tf file # Collect all module directories containing a main.tf file
for path in $(find . -maxdepth 2 -not -path '*/.*' -type f -name main.tf | cut -d '/' -f 2 | sort -u); do for path in $(find . -maxdepth 2 -not -path '*/.*' -type f -name main.tf | cut -d '/' -f 2 | sort -u); do
modules+=("${path}") modules+=("${path}")
done done
echo "Checking modules: ${modules[*]}" echo "Checking modules: ${modules[*]}"
# Function to update the component status on Instatus # Function to update the component status on Instatus
update_component_status() { update_component_status() {
local component_status=$1 local component_status=$1
# see https://instatus.com/help/api/components # see https://instatus.com/help/api/components
(curl -X PUT "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/components/$INSTATUS_COMPONENT_ID" \ (curl -X PUT "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/components/$INSTATUS_COMPONENT_ID" \
-H "Authorization: Bearer $INSTATUS_API_KEY" \ -H "Authorization: Bearer $INSTATUS_API_KEY" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "{\"status\": \"$component_status\"}") -d "{\"status\": \"$component_status\"}")
} }
# Function to create an incident # Function to create an incident
create_incident() { create_incident() {
local incident_name="Degraded Service" local incident_name="Degraded Service"
local message="The following modules are experiencing issues:\n" local message="The following modules are experiencing issues:\n"
for i in "${!failures[@]}"; do for i in "${!failures[@]}"; do
message+="$((i + 1)). ${failures[$i]}\n" message+="$((i + 1)). ${failures[$i]}\n"
done done
component_status="PARTIALOUTAGE" component_status="PARTIALOUTAGE"
if ((${#failures[@]} == ${#modules[@]})); then if ((${#failures[@]} == ${#modules[@]})); then
component_status="MAJOROUTAGE" component_status="MAJOROUTAGE"
fi fi
# see https://instatus.com/help/api/incidents # see https://instatus.com/help/api/incidents
incident_id=$(curl -s -X POST "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/incidents" \ incident_id=$(curl -s -X POST "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/incidents" \
-H "Authorization: Bearer $INSTATUS_API_KEY" \ -H "Authorization: Bearer $INSTATUS_API_KEY" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "{ -d "{
\"name\": \"$incident_name\", \"name\": \"$incident_name\",
\"message\": \"$message\", \"message\": \"$message\",
\"components\": [\"$INSTATUS_COMPONENT_ID\"], \"components\": [\"$INSTATUS_COMPONENT_ID\"],
@ -76,129 +76,129 @@ create_incident() {
] ]
}" | jq -r '.id') }" | jq -r '.id')
echo "Created incident with ID: $incident_id" echo "Created incident with ID: $incident_id"
} }
# Function to check for existing unresolved incidents # Function to check for existing unresolved incidents
check_existing_incident() { check_existing_incident() {
# Fetch the latest incidents with status not equal to "RESOLVED" # Fetch the latest incidents with status not equal to "RESOLVED"
local unresolved_incidents=$(curl -s -X GET "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/incidents" \ local unresolved_incidents=$(curl -s -X GET "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/incidents" \
-H "Authorization: Bearer $INSTATUS_API_KEY" \ -H "Authorization: Bearer $INSTATUS_API_KEY" \
-H "Content-Type: application/json" | jq -r '.incidents[] | select(.status != "RESOLVED") | .id') -H "Content-Type: application/json" | jq -r '.incidents[] | select(.status != "RESOLVED") | .id')
if [[ -n "$unresolved_incidents" ]]; then if [[ -n "$unresolved_incidents" ]]; then
echo "Unresolved incidents found: $unresolved_incidents" echo "Unresolved incidents found: $unresolved_incidents"
return 0 # Indicate that there are unresolved incidents return 0 # Indicate that there are unresolved incidents
else else
echo "No unresolved incidents found." echo "No unresolved incidents found."
return 1 # Indicate that no unresolved incidents exist return 1 # Indicate that no unresolved incidents exist
fi fi
} }
force_redeploy_registry() { force_redeploy_registry() {
# These are not secret values; safe to just expose directly in script # These are not secret values; safe to just expose directly in script
local VERCEL_TEAM_SLUG="codercom" local VERCEL_TEAM_SLUG="codercom"
local VERCEL_TEAM_ID="team_tGkWfhEGGelkkqUUm9nXq17r" local VERCEL_TEAM_ID="team_tGkWfhEGGelkkqUUm9nXq17r"
local VERCEL_APP="registry" local VERCEL_APP="registry"
local latest_res local latest_res
latest_res=$( latest_res=$(
curl "https://api.vercel.com/v6/deployments?app=$VERCEL_APP&limit=1&slug=$VERCEL_TEAM_SLUG&teamId=$VERCEL_TEAM_ID&target=production&state=BUILDING,INITIALIZING,QUEUED,READY" \ curl "https://api.vercel.com/v6/deployments?app=$VERCEL_APP&limit=1&slug=$VERCEL_TEAM_SLUG&teamId=$VERCEL_TEAM_ID&target=production&state=BUILDING,INITIALIZING,QUEUED,READY" \
--fail \ --fail \
--silent \ --silent \
--header "Authorization: Bearer $VERCEL_API_KEY" \ --header "Authorization: Bearer $VERCEL_API_KEY" \
--header "Content-Type: application/json" --header "Content-Type: application/json"
) )
# If we have zero deployments, something is VERY wrong. Make the whole # If we have zero deployments, something is VERY wrong. Make the whole
# script exit with a non-zero status code # script exit with a non-zero status code
local latest_id local latest_id
latest_id=$(echo "${latest_res}" | jq -r '.deployments[0].uid') latest_id=$(echo "${latest_res}" | jq -r '.deployments[0].uid')
if [[ "${latest_id}" = "null" ]]; then if [[ "${latest_id}" = "null" ]]; then
echo "Unable to pull any previous deployments for redeployment" echo "Unable to pull any previous deployments for redeployment"
echo "Please redeploy the latest deployment manually in Vercel." echo "Please redeploy the latest deployment manually in Vercel."
echo "https://vercel.com/codercom/registry/deployments" echo "https://vercel.com/codercom/registry/deployments"
exit 1 exit 1
fi fi
local latest_date_ts_seconds local latest_date_ts_seconds
latest_date_ts_seconds=$(echo "${latest_res}" | jq -r '.deployments[0].createdAt/1000|floor') latest_date_ts_seconds=$(echo "${latest_res}" | jq -r '.deployments[0].createdAt/1000|floor')
local current_date_ts_seconds local current_date_ts_seconds
current_date_ts_seconds="$(date +%s)" current_date_ts_seconds="$(date +%s)"
local max_redeploy_interval_seconds=7200 # 2 hours local max_redeploy_interval_seconds=7200 # 2 hours
if ((current_date_ts_seconds - latest_date_ts_seconds < max_redeploy_interval_seconds)); then if ((current_date_ts_seconds - latest_date_ts_seconds < max_redeploy_interval_seconds)); then
echo "The registry was deployed less than 2 hours ago." echo "The registry was deployed less than 2 hours ago."
echo "Not automatically re-deploying the regitstry." echo "Not automatically re-deploying the regitstry."
echo "A human reading this message should decide if a redeployment is necessary." echo "A human reading this message should decide if a redeployment is necessary."
echo "Please check the Vercel dashboard for more information." echo "Please check the Vercel dashboard for more information."
echo "https://vercel.com/codercom/registry/deployments" echo "https://vercel.com/codercom/registry/deployments"
exit 1 exit 1
fi fi
local latest_deployment_state local latest_deployment_state
latest_deployment_state="$(echo "${latest_res}" | jq -r '.deployments[0].state')" latest_deployment_state="$(echo "${latest_res}" | jq -r '.deployments[0].state')"
if [[ "${latest_deployment_state}" != "READY" ]]; then if [[ "${latest_deployment_state}" != "READY" ]]; then
echo "Last deployment was not in READY state. Skipping redeployment." echo "Last deployment was not in READY state. Skipping redeployment."
echo "A human reading this message should decide if a redeployment is necessary." echo "A human reading this message should decide if a redeployment is necessary."
echo "Please check the Vercel dashboard for more information." echo "Please check the Vercel dashboard for more information."
echo "https://vercel.com/codercom/registry/deployments" echo "https://vercel.com/codercom/registry/deployments"
exit 1 exit 1
fi fi
echo "=============================================================" echo "============================================================="
echo "!!! Redeploying registry with deployment ID: ${latest_id} !!!" echo "!!! Redeploying registry with deployment ID: ${latest_id} !!!"
echo "=============================================================" echo "============================================================="
if ! curl -X POST "https://api.vercel.com/v13/deployments?forceNew=1&skipAutoDetectionConfirmation=1&slug=$VERCEL_TEAM_SLUG&teamId=$VERCEL_TEAM_ID" \ if ! curl -X POST "https://api.vercel.com/v13/deployments?forceNew=1&skipAutoDetectionConfirmation=1&slug=$VERCEL_TEAM_SLUG&teamId=$VERCEL_TEAM_ID" \
--fail \ --fail \
--header "Authorization: Bearer $VERCEL_API_KEY" \ --header "Authorization: Bearer $VERCEL_API_KEY" \
--header "Content-Type: application/json" \ --header "Content-Type: application/json" \
--data-raw "{ \"deploymentId\": \"${latest_id}\", \"name\": \"${VERCEL_APP}\", \"target\": \"production\" }"; then --data-raw "{ \"deploymentId\": \"${latest_id}\", \"name\": \"${VERCEL_APP}\", \"target\": \"production\" }"; then
echo "DEPLOYMENT FAILED! Please check the Vercel dashboard for more information." echo "DEPLOYMENT FAILED! Please check the Vercel dashboard for more information."
echo "https://vercel.com/codercom/registry/deployments" echo "https://vercel.com/codercom/registry/deployments"
exit 1 exit 1
fi fi
} }
# Check each module's accessibility # Check each module's accessibility
for module in "${modules[@]}"; do for module in "${modules[@]}"; do
# Trim leading/trailing whitespace from module name # Trim leading/trailing whitespace from module name
module=$(echo "${module}" | xargs) module=$(echo "${module}" | xargs)
url="${REGISTRY_BASE_URL}/modules/${module}" url="${REGISTRY_BASE_URL}/modules/${module}"
printf "=== Checking module %s at %s\n" "${module}" "${url}" printf "=== Checking module %s at %s\n" "${module}" "${url}"
status_code=$(curl --output /dev/null --head --silent --fail --location "${url}" --retry 3 --write-out "%{http_code}") status_code=$(curl --output /dev/null --head --silent --fail --location "${url}" --retry 3 --write-out "%{http_code}")
if ((status_code != 200)); then if ((status_code != 200)); then
printf "==> FAIL(%s)\n" "${status_code}" printf "==> FAIL(%s)\n" "${status_code}"
status=1 status=1
failures+=("${module}") failures+=("${module}")
else else
printf "==> OK(%s)\n" "${status_code}" printf "==> OK(%s)\n" "${status_code}"
fi fi
done done
# Determine overall status and update Instatus component # Determine overall status and update Instatus component
if ((status == 0)); then if ((status == 0)); then
echo "All modules are operational." echo "All modules are operational."
# set to # set to
update_component_status "OPERATIONAL" update_component_status "OPERATIONAL"
else else
echo "The following modules have issues: ${failures[*]}" echo "The following modules have issues: ${failures[*]}"
# check if all modules are down # check if all modules are down
if ((${#failures[@]} == ${#modules[@]})); then if ((${#failures[@]} == ${#modules[@]})); then
update_component_status "MAJOROUTAGE" update_component_status "MAJOROUTAGE"
else else
update_component_status "PARTIALOUTAGE" update_component_status "PARTIALOUTAGE"
fi fi
# Check if there is an existing incident before creating a new one # Check if there is an existing incident before creating a new one
if ! check_existing_incident; then if ! check_existing_incident; then
create_incident create_incident
fi fi
# If a module is down, force a reployment to try getting things back online # If a module is down, force a reployment to try getting things back online
# ASAP # ASAP
# EDIT: registry.coder.com is no longer hosted on vercel # EDIT: registry.coder.com is no longer hosted on vercel
#force_redeploy_registry #force_redeploy_registry
fi fi
exit "${status}" exit "${status}"

4
.github/typos.toml vendored Normal file
View File

@ -0,0 +1,4 @@
[default.extend-words]
muc = "muc" # For Munich location code
Hashi = "Hashi"
HashiCorp = "HashiCorp"

View File

@ -7,20 +7,8 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs: jobs:
validate-readme-files:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.23.2"
- name: Validate contributors
run: go build ./scripts/contributors && ./contributors
- name: Remove build file artifact
run: rm ./contributors
test-terraform: test-terraform:
name: Validate Terraform output
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out code - name: Check out code
@ -42,3 +30,41 @@ jobs:
run: bun test run: bun test
- name: Run Terraform Validate - name: Run Terraform Validate
run: bun terraform-validate run: bun terraform-validate
validate-style:
name: Check for typos and unformatted code
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Install Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
# Need Terraform for its formatter
- name: Install Terraform
uses: coder/coder/.github/actions/setup-tf@main
- name: Install dependencies
run: bun install
- name: Validate formatting
run: bun fmt:ci
- name: Check for typos
uses: crate-ci/typos@v1.31.1
with:
config: .github/typos.toml
validate-readme-files:
name: Validate README files
runs-on: ubuntu-latest
# We want to do some basic README checks first before we try analyzing the
# contents
needs: validate-style
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.23.2"
- name: Validate contributors
run: go build ./scripts/contributors && ./contributors
- name: Remove build file artifact
run: rm ./contributors

View File

@ -11,10 +11,10 @@ BOLD='\033[0;1m'
printf "$${BOLD}Installing MODULE_NAME ...\n\n" printf "$${BOLD}Installing MODULE_NAME ...\n\n"
# Add code here # Add code here
# Use varibles from the templatefile function in main.tf # Use variables from the templatefile function in main.tf
# e.g. LOG_PATH, PORT, etc. # e.g. LOG_PATH, PORT, etc.
printf "🥳 Installation comlete!\n\n" printf "🥳 Installation complete!\n\n"
printf "👷 Starting MODULE_NAME in background...\n\n" printf "👷 Starting MODULE_NAME in background...\n\n"
# Start the app in here # Start the app in here

View File

@ -1,10 +1,11 @@
{ {
"name": "modules", "name": "modules",
"scripts": { "scripts": {
"test": "bun test", "fmt": "bun x prettier --write **/*.sh **/*.ts **/*.md *.md && terraform fmt -recursive -diff",
"fmt:ci": "bun x prettier --check **/*.sh **/*.ts **/*.md *.md && terraform fmt -check -recursive -diff",
"terraform-validate": "./scripts/terraform_validate.sh", "terraform-validate": "./scripts/terraform_validate.sh",
"fmt": "bun x prettier -w **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt **/*.tf .sample/main.tf", "test": "bun test",
"fmt:ci": "bun x prettier --check **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt -check **/*.tf .sample/main.tf" "update-version": "./update-version.sh"
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "^1.2.9", "@types/bun": "^1.2.9",

View File

@ -3,35 +3,35 @@
set -euo pipefail set -euo pipefail
validate_terraform_directory() { validate_terraform_directory() {
local dir="$1" local dir="$1"
echo "Running \`terraform validate\` in $dir" echo "Running \`terraform validate\` in $dir"
pushd "$dir" pushd "$dir"
terraform init -upgrade terraform init -upgrade
terraform validate terraform validate
popd popd
} }
main() { main() {
# Get the directory of the script # Get the directory of the script
local script_dir=$(dirname "$(readlink -f "$0")") local script_dir=$(dirname "$(readlink -f "$0")")
# Code assumes that registry directory will always be in same position # Code assumes that registry directory will always be in same position
# relative to the main script directory # relative to the main script directory
local registry_dir="$script_dir/../registry" local registry_dir="$script_dir/../registry"
# Get all subdirectories in the registry directory. Code assumes that # Get all subdirectories in the registry directory. Code assumes that
# Terraform directories won't begin to appear until three levels deep into # Terraform directories won't begin to appear until three levels deep into
# the registry (e.g., registry/coder/modules/coder-login, which will then # the registry (e.g., registry/coder/modules/coder-login, which will then
# have a main.tf file inside it) # have a main.tf file inside it)
local subdirs=$(find "$registry_dir" -mindepth 3 -type d | sort) local subdirs=$(find "$registry_dir" -mindepth 3 -type d | sort)
for dir in $subdirs; do for dir in $subdirs; do
# Skip over any directories that obviously don't have the necessary # Skip over any directories that obviously don't have the necessary
# files # files
if test -f "$dir/main.tf"; then if test -f "$dir/main.tf"; then
validate_terraform_directory "$dir" validate_terraform_directory "$dir"
fi fi
done done
} }
main main