diff --git a/.github/actions/embedded-pg-cache/download/action.yml b/.github/actions/embedded-pg-cache/download/action.yml index c2c3c0c0b2..854e5045c2 100644 --- a/.github/actions/embedded-pg-cache/download/action.yml +++ b/.github/actions/embedded-pg-cache/download/action.yml @@ -25,9 +25,11 @@ runs: export YEAR_MONTH=$(date +'%Y-%m') export PREV_YEAR_MONTH=$(date -d 'last month' +'%Y-%m') export DAY=$(date +'%d') - echo "year-month=$YEAR_MONTH" >> $GITHUB_OUTPUT - echo "prev-year-month=$PREV_YEAR_MONTH" >> $GITHUB_OUTPUT - echo "cache-key=${{ inputs.key-prefix }}-${YEAR_MONTH}-${DAY}" >> $GITHUB_OUTPUT + echo "year-month=$YEAR_MONTH" >> "$GITHUB_OUTPUT" + echo "prev-year-month=$PREV_YEAR_MONTH" >> "$GITHUB_OUTPUT" + echo "cache-key=${INPUTS_KEY_PREFIX}-${YEAR_MONTH}-${DAY}" >> "$GITHUB_OUTPUT" + env: + INPUTS_KEY_PREFIX: ${{ inputs.key-prefix }} # By default, depot keeps caches for 14 days. This is plenty for embedded # postgres, which changes infrequently. diff --git a/.github/actions/test-cache/download/action.yml b/.github/actions/test-cache/download/action.yml index 06a87fee06..623bb61e11 100644 --- a/.github/actions/test-cache/download/action.yml +++ b/.github/actions/test-cache/download/action.yml @@ -27,9 +27,11 @@ runs: export YEAR_MONTH=$(date +'%Y-%m') export PREV_YEAR_MONTH=$(date -d 'last month' +'%Y-%m') export DAY=$(date +'%d') - echo "year-month=$YEAR_MONTH" >> $GITHUB_OUTPUT - echo "prev-year-month=$PREV_YEAR_MONTH" >> $GITHUB_OUTPUT - echo "cache-key=${{ inputs.key-prefix }}-${YEAR_MONTH}-${DAY}" >> $GITHUB_OUTPUT + echo "year-month=$YEAR_MONTH" >> "$GITHUB_OUTPUT" + echo "prev-year-month=$PREV_YEAR_MONTH" >> "$GITHUB_OUTPUT" + echo "cache-key=${INPUTS_KEY_PREFIX}-${YEAR_MONTH}-${DAY}" >> "$GITHUB_OUTPUT" + env: + INPUTS_KEY_PREFIX: ${{ inputs.key-prefix }} # TODO: As a cost optimization, we could remove caches that are older than # a day or two. By default, depot keeps caches for 14 days, which isn't diff --git a/.github/actions/upload-datadog/action.yaml b/.github/actions/upload-datadog/action.yaml index a2df93ab14..274ff3df64 100644 --- a/.github/actions/upload-datadog/action.yaml +++ b/.github/actions/upload-datadog/action.yaml @@ -12,13 +12,12 @@ runs: run: | set -e - owner=${{ github.repository_owner }} - echo "owner: $owner" - if [[ $owner != "coder" ]]; then + echo "owner: $REPO_OWNER" + if [[ "$REPO_OWNER" != "coder" ]]; then echo "Not a pull request from the main repo, skipping..." exit 0 fi - if [[ -z "${{ inputs.api-key }}" ]]; then + if [[ -z "${DATADOG_API_KEY}" ]]; then # This can happen for dependabot. echo "No API key provided, skipping..." exit 0 @@ -31,37 +30,38 @@ runs: TMP_DIR=$(mktemp -d) - if [[ "${{ runner.os }}" == "Windows" ]]; then + if [[ "${RUNNER_OS}" == "Windows" ]]; then BINARY_PATH="${TMP_DIR}/datadog-ci.exe" BINARY_URL="https://github.com/DataDog/datadog-ci/releases/download/${BINARY_VERSION}/datadog-ci_win-x64" - elif [[ "${{ runner.os }}" == "macOS" ]]; then + elif [[ "${RUNNER_OS}" == "macOS" ]]; then BINARY_PATH="${TMP_DIR}/datadog-ci" BINARY_URL="https://github.com/DataDog/datadog-ci/releases/download/${BINARY_VERSION}/datadog-ci_darwin-arm64" - elif [[ "${{ runner.os }}" == "Linux" ]]; then + elif [[ "${RUNNER_OS}" == "Linux" ]]; then BINARY_PATH="${TMP_DIR}/datadog-ci" BINARY_URL="https://github.com/DataDog/datadog-ci/releases/download/${BINARY_VERSION}/datadog-ci_linux-x64" else - echo "Unsupported OS: ${{ runner.os }}" + echo "Unsupported OS: $RUNNER_OS" exit 1 fi - echo "Downloading DataDog CI binary version ${BINARY_VERSION} for ${{ runner.os }}..." + echo "Downloading DataDog CI binary version ${BINARY_VERSION} for $RUNNER_OS..." curl -sSL "$BINARY_URL" -o "$BINARY_PATH" - if [[ "${{ runner.os }}" == "Windows" ]]; then + if [[ "${RUNNER_OS}" == "Windows" ]]; then echo "$BINARY_HASH_WINDOWS $BINARY_PATH" | sha256sum --check - elif [[ "${{ runner.os }}" == "macOS" ]]; then + elif [[ "${RUNNER_OS}" == "macOS" ]]; then echo "$BINARY_HASH_MACOS $BINARY_PATH" | shasum -a 256 --check - elif [[ "${{ runner.os }}" == "Linux" ]]; then + elif [[ "${RUNNER_OS}" == "Linux" ]]; then echo "$BINARY_HASH_LINUX $BINARY_PATH" | sha256sum --check fi # Make binary executable (not needed for Windows) - if [[ "${{ runner.os }}" != "Windows" ]]; then + if [[ "${RUNNER_OS}" != "Windows" ]]; then chmod +x "$BINARY_PATH" fi "$BINARY_PATH" junit upload --service coder ./gotests.xml \ - --tags os:${{runner.os}} --tags runner_name:${{runner.name}} + --tags "os:${RUNNER_OS}" --tags "runner_name:${RUNNER_NAME}" env: + REPO_OWNER: ${{ github.repository_owner }} DATADOG_API_KEY: ${{ inputs.api-key }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1d9f1ac0ef..76becb50ad 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -42,7 +42,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 - # For pull requests it's not necessary to checkout the code + persist-credentials: false - name: check changed files uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 id: filter @@ -111,7 +111,9 @@ jobs: - id: debug run: | - echo "${{ toJSON(steps.filter )}}" + echo "$FILTER_JSON" + env: + FILTER_JSON: ${{ toJSON(steps.filter.outputs) }} # Disabled due to instability. See: https://github.com/coder/coder/issues/14553 # Re-enable once the flake hash calculation is stable. @@ -162,6 +164,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false - name: Setup Node uses: ./.github/actions/setup-node @@ -171,10 +174,10 @@ jobs: - name: Get golangci-lint cache dir run: | - linter_ver=$(egrep -o 'GOLANGCI_LINT_VERSION=\S+' dogfood/coder/Dockerfile | cut -d '=' -f 2) - go install github.com/golangci/golangci-lint/cmd/golangci-lint@v$linter_ver + linter_ver=$(grep -Eo 'GOLANGCI_LINT_VERSION=\S+' dogfood/coder/Dockerfile | cut -d '=' -f 2) + go install "github.com/golangci/golangci-lint/cmd/golangci-lint@v$linter_ver" dir=$(golangci-lint cache status | awk '/Dir/ { print $2 }') - echo "LINT_CACHE_DIR=$dir" >> $GITHUB_ENV + echo "LINT_CACHE_DIR=$dir" >> "$GITHUB_ENV" - name: golangci-lint cache uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 @@ -206,7 +209,12 @@ jobs: - name: make lint run: | - make --output-sync=line -j lint + # zizmor isn't included in the lint target because it takes a while, + # but we explicitly want to run it in CI. + make --output-sync=line -j lint lint/actions/zizmor + env: + # Used by zizmor to lint third-party GitHub actions. + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check workflow files run: | @@ -234,6 +242,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false - name: Setup Node uses: ./.github/actions/setup-node @@ -289,6 +298,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false - name: Setup Node uses: ./.github/actions/setup-node @@ -305,8 +315,8 @@ jobs: - name: make fmt run: | - export PATH=${PATH}:$(go env GOPATH)/bin - make --output-sync -j -B fmt + PATH="${PATH}:$(go env GOPATH)/bin" \ + make --output-sync -j -B fmt - name: Check for unstaged files run: ./scripts/check_unstaged.sh @@ -340,8 +350,8 @@ jobs: - name: Disable Spotlight Indexing if: runner.os == 'macOS' run: | - enabled=$(sudo mdutil -a -s | grep "Indexing enabled" | wc -l) - if [ $enabled -eq 0 ]; then + enabled=$(sudo mdutil -a -s | { grep -Fc "Indexing enabled" || true; }) + if [ "$enabled" -eq 0 ]; then echo "Spotlight indexing is already disabled" exit 0 fi @@ -353,12 +363,13 @@ jobs: # a separate repository to allow its use before actions/checkout. - name: Setup RAM Disks if: runner.os == 'Windows' - uses: coder/setup-ramdisk-action@e1100847ab2d7bcd9d14bcda8f2d1b0f07b36f1b + uses: coder/setup-ramdisk-action@e1100847ab2d7bcd9d14bcda8f2d1b0f07b36f1b # v0.1.0 - name: Checkout uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false - name: Setup Go Paths id: go-paths @@ -421,34 +432,34 @@ jobs: set -o errexit set -o pipefail - if [ "${{ runner.os }}" == "Windows" ]; then + if [ "$RUNNER_OS" == "Windows" ]; then # Create a temp dir on the R: ramdisk drive for Windows. The default # C: drive is extremely slow: https://github.com/actions/runner-images/issues/8755 mkdir -p "R:/temp/embedded-pg" go run scripts/embedded-pg/main.go -path "R:/temp/embedded-pg" -cache "${EMBEDDED_PG_CACHE_DIR}" - elif [ "${{ runner.os }}" == "macOS" ]; then + elif [ "$RUNNER_OS" == "macOS" ]; then # Postgres runs faster on a ramdisk on macOS too mkdir -p /tmp/tmpfs sudo mount_tmpfs -o noowners -s 8g /tmp/tmpfs go run scripts/embedded-pg/main.go -path /tmp/tmpfs/embedded-pg -cache "${EMBEDDED_PG_CACHE_DIR}" - elif [ "${{ runner.os }}" == "Linux" ]; then + elif [ "$RUNNER_OS" == "Linux" ]; then make test-postgres-docker fi # if macOS, install google-chrome for scaletests # As another concern, should we really have this kind of external dependency # requirement on standard CI? - if [ "${{ matrix.os }}" == "macos-latest" ]; then + if [ "${RUNNER_OS}" == "macOS" ]; then brew install google-chrome fi # macOS will output "The default interactive shell is now zsh" # intermittently in CI... - if [ "${{ matrix.os }}" == "macos-latest" ]; then + if [ "${RUNNER_OS}" == "macOS" ]; then touch ~/.bash_profile && echo "export BASH_SILENCE_DEPRECATION_WARNING=1" >> ~/.bash_profile fi - if [ "${{ runner.os }}" == "Windows" ]; then + if [ "${RUNNER_OS}" == "Windows" ]; then # Our Windows runners have 16 cores. # On Windows Postgres chokes up when we have 16x16=256 tests # running in parallel, and dbtestutil.NewDB starts to take more than @@ -458,7 +469,7 @@ jobs: NUM_PARALLEL_TESTS=16 # Only the CLI and Agent are officially supported on Windows and the rest are too flaky PACKAGES="./cli/... ./enterprise/cli/... ./agent/..." - elif [ "${{ runner.os }}" == "macOS" ]; then + elif [ "${RUNNER_OS}" == "macOS" ]; then # Our macOS runners have 8 cores. We set NUM_PARALLEL_TESTS to 16 # because the tests complete faster and Postgres doesn't choke. It seems # that macOS's tmpfs is faster than the one on Windows. @@ -466,7 +477,7 @@ jobs: NUM_PARALLEL_TESTS=16 # Only the CLI and Agent are officially supported on macOS and the rest are too flaky PACKAGES="./cli/... ./enterprise/cli/... ./agent/..." - elif [ "${{ runner.os }}" == "Linux" ]; then + elif [ "${RUNNER_OS}" == "Linux" ]; then # Our Linux runners have 8 cores. NUM_PARALLEL_PACKAGES=8 NUM_PARALLEL_TESTS=8 @@ -475,7 +486,7 @@ jobs: # by default, run tests with cache TESTCOUNT="" - if [ "${{ github.ref }}" == "refs/heads/main" ]; then + if [ "${GITHUB_REF}" == "refs/heads/main" ]; then # on main, run tests without cache TESTCOUNT="-count=1" fi @@ -485,7 +496,7 @@ jobs: # terraform gets installed in a random directory, so we need to normalize # the path to the terraform binary or a bunch of cached tests will be # invalidated. See scripts/normalize_path.sh for more details. - normalize_path_with_symlinks "$RUNNER_TEMP/sym" "$(dirname $(which terraform))" + normalize_path_with_symlinks "$RUNNER_TEMP/sym" "$(dirname "$(which terraform)")" gotestsum --format standard-quiet --packages "$PACKAGES" \ -- -timeout=20m -v -p $NUM_PARALLEL_PACKAGES -parallel=$NUM_PARALLEL_TESTS $TESTCOUNT @@ -546,6 +557,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false - name: Setup Go uses: ./.github/actions/setup-go @@ -594,6 +606,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false - name: Setup Go uses: ./.github/actions/setup-go @@ -653,6 +666,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false - name: Setup Go uses: ./.github/actions/setup-go @@ -679,11 +693,12 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false - name: Setup Node uses: ./.github/actions/setup-node - - run: pnpm test:ci --max-workers $(nproc) + - run: pnpm test:ci --max-workers "$(nproc)" working-directory: site test-e2e: @@ -711,6 +726,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false - name: Setup Node uses: ./.github/actions/setup-node @@ -785,6 +801,7 @@ jobs: fetch-depth: 0 # 👇 Tells the checkout which commit hash to reference ref: ${{ github.event.pull_request.head.ref }} + persist-credentials: false - name: Setup Node uses: ./.github/actions/setup-node @@ -863,6 +880,7 @@ jobs: with: # 0 is required here for version.sh to work. fetch-depth: 0 + persist-credentials: false - name: Setup Node uses: ./.github/actions/setup-node @@ -927,7 +945,7 @@ jobs: egress-policy: audit - name: Ensure required checks - run: | + run: | # zizmor: ignore[template-injection] We're just reading needs.x.result here, no risk of injection echo "Checking required checks" echo "- fmt: ${{ needs.fmt.result }}" echo "- lint: ${{ needs.lint.result }}" @@ -961,13 +979,16 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false - name: Setup build tools run: | brew install bash gnu-getopt make - echo "$(brew --prefix bash)/bin" >> $GITHUB_PATH - echo "$(brew --prefix gnu-getopt)/bin" >> $GITHUB_PATH - echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH + { + echo "$(brew --prefix bash)/bin" + echo "$(brew --prefix gnu-getopt)/bin" + echo "$(brew --prefix make)/libexec/gnubin" + } >> "$GITHUB_PATH" - name: Switch XCode Version uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 @@ -1045,6 +1066,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false - name: Setup Node uses: ./.github/actions/setup-node @@ -1099,6 +1121,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false - name: GHCR Login uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 @@ -1196,8 +1219,8 @@ jobs: go mod download version="$(./scripts/version.sh)" - tag="main-$(echo "$version" | sed 's/+/-/g')" - echo "tag=$tag" >> $GITHUB_OUTPUT + tag="main-${version//+/-}" + echo "tag=$tag" >> "$GITHUB_OUTPUT" make gen/mark-fresh make -j \ @@ -1233,15 +1256,15 @@ jobs: # build Docker images for each architecture version="$(./scripts/version.sh)" - tag="main-$(echo "$version" | sed 's/+/-/g')" - echo "tag=$tag" >> $GITHUB_OUTPUT + tag="main-${version//+/-}" + echo "tag=$tag" >> "$GITHUB_OUTPUT" # build images for each architecture # note: omitting the -j argument to avoid race conditions when pushing make build/coder_"$version"_linux_{amd64,arm64,armv7}.tag # only push if we are on main branch - if [ "${{ github.ref }}" == "refs/heads/main" ]; then + if [ "${GITHUB_REF}" == "refs/heads/main" ]; then # build and push multi-arch manifest, this depends on the other images # being pushed so will automatically push them # note: omitting the -j argument to avoid race conditions when pushing @@ -1254,10 +1277,11 @@ jobs: # we are adding `latest` tag and keeping `main` for backward # compatibality for t in "${tags[@]}"; do + # shellcheck disable=SC2046 ./scripts/build_docker_multiarch.sh \ --push \ --target "ghcr.io/coder/coder-preview:$t" \ - --version $version \ + --version "$version" \ $(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag) done fi @@ -1267,12 +1291,13 @@ jobs: continue-on-error: true env: COSIGN_EXPERIMENTAL: 1 + BUILD_TAG: ${{ steps.build-docker.outputs.tag }} run: | set -euxo pipefail # Define image base and tags IMAGE_BASE="ghcr.io/coder/coder-preview" - TAGS=("${{ steps.build-docker.outputs.tag }}" "main" "latest") + TAGS=("${BUILD_TAG}" "main" "latest") # Generate and attest SBOM for each tag for tag in "${TAGS[@]}"; do @@ -1411,7 +1436,7 @@ jobs: # Report attestation failures but don't fail the workflow - name: Check attestation status if: github.ref == 'refs/heads/main' - run: | + run: | # zizmor: ignore[template-injection] We're just reading steps.attest_x.outcome here, no risk of injection if [[ "${{ steps.attest_main.outcome }}" == "failure" ]]; then echo "::warning::GitHub attestation for main tag failed" fi @@ -1471,6 +1496,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false - name: Authenticate to Google Cloud uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12 @@ -1535,6 +1561,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false - name: Setup flyctl uses: superfly/flyctl-actions/setup-flyctl@fc53c09e1bc3be6f54706524e3b82c4f462f77be # v1.5 @@ -1570,7 +1597,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 - # We need golang to run the migration main.go + persist-credentials: false - name: Setup Go uses: ./.github/actions/setup-go @@ -1606,15 +1633,15 @@ jobs: "fields": [ { "type": "mrkdwn", - "text": "*Workflow:*\n${{ github.workflow }}" + "text": "*Workflow:*\n'"${GITHUB_WORKFLOW}"'" }, { "type": "mrkdwn", - "text": "*Committer:*\n${{ github.actor }}" + "text": "*Committer:*\n'"${GITHUB_ACTOR}"'" }, { "type": "mrkdwn", - "text": "*Commit:*\n${{ github.sha }}" + "text": "*Commit:*\n'"${GITHUB_SHA}"'" } ] }, @@ -1622,7 +1649,7 @@ jobs: "type": "section", "text": { "type": "mrkdwn", - "text": "*View failure:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Click here>" + "text": "*View failure:* <'"${RUN_URL}"'|Click here>" } }, { @@ -1633,4 +1660,7 @@ jobs: } } ] - }' ${{ secrets.CI_FAILURE_SLACK_WEBHOOK }} + }' "${SLACK_WEBHOOK}" + env: + SLACK_WEBHOOK: ${{ secrets.CI_FAILURE_SLACK_WEBHOOK }} + RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" diff --git a/.github/workflows/contrib.yaml b/.github/workflows/contrib.yaml index 27dffe94f4..e9c5c9ec2a 100644 --- a/.github/workflows/contrib.yaml +++ b/.github/workflows/contrib.yaml @@ -3,6 +3,7 @@ name: contrib on: issue_comment: types: [created, edited] + # zizmor: ignore[dangerous-triggers] We explicitly want to run on pull_request_target. pull_request_target: types: - opened diff --git a/.github/workflows/dependabot.yaml b/.github/workflows/dependabot.yaml index f86601096a..f95ae3fa81 100644 --- a/.github/workflows/dependabot.yaml +++ b/.github/workflows/dependabot.yaml @@ -15,7 +15,7 @@ jobs: github.event_name == 'pull_request' && github.event.action == 'opened' && github.event.pull_request.user.login == 'dependabot[bot]' && - github.actor_id == 49699333 && + github.event.pull_request.user.id == 49699333 && github.repository == 'coder/coder' permissions: pull-requests: write @@ -44,10 +44,6 @@ jobs: GH_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Send Slack notification - env: - PR_URL: ${{github.event.pull_request.html_url}} - PR_TITLE: ${{github.event.pull_request.title}} - PR_NUMBER: ${{github.event.pull_request.number}} run: | curl -X POST -H 'Content-type: application/json' \ --data '{ @@ -58,7 +54,7 @@ jobs: "type": "header", "text": { "type": "plain_text", - "text": ":pr-merged: Auto merge enabled for Dependabot PR #${{ env.PR_NUMBER }}", + "text": ":pr-merged: Auto merge enabled for Dependabot PR #'"${PR_NUMBER}"'", "emoji": true } }, @@ -67,7 +63,7 @@ jobs: "fields": [ { "type": "mrkdwn", - "text": "${{ env.PR_TITLE }}" + "text": "'"${PR_TITLE}"'" } ] }, @@ -80,9 +76,14 @@ jobs: "type": "plain_text", "text": "View PR" }, - "url": "${{ env.PR_URL }}" + "url": "'"${PR_URL}"'" } ] } ] - }' ${{ secrets.DEPENDABOT_PRS_SLACK_WEBHOOK }} + }' "${{ secrets.DEPENDABOT_PRS_SLACK_WEBHOOK }}" + env: + SLACK_WEBHOOK: ${{ secrets.DEPENDABOT_PRS_SLACK_WEBHOOK }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_URL: ${{ github.event.pull_request.html_url }} diff --git a/.github/workflows/docker-base.yaml b/.github/workflows/docker-base.yaml index dd36ab5a45..5c8fa14245 100644 --- a/.github/workflows/docker-base.yaml +++ b/.github/workflows/docker-base.yaml @@ -44,6 +44,8 @@ jobs: - name: Checkout uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false - name: Docker login uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 diff --git a/.github/workflows/docs-ci.yaml b/.github/workflows/docs-ci.yaml index cba5bcbcd2..887db40660 100644 --- a/.github/workflows/docs-ci.yaml +++ b/.github/workflows/docs-ci.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false - name: Setup Node uses: ./.github/actions/setup-node @@ -39,10 +41,16 @@ jobs: - name: lint if: steps.changed-files.outputs.any_changed == 'true' run: | - pnpm exec markdownlint-cli2 ${{ steps.changed-files.outputs.all_changed_files }} + # shellcheck disable=SC2086 + pnpm exec markdownlint-cli2 $ALL_CHANGED_FILES + env: + ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} - name: fmt if: steps.changed-files.outputs.any_changed == 'true' run: | # markdown-table-formatter requires a space separated list of files - echo ${{ steps.changed-files.outputs.all_changed_files }} | tr ',' '\n' | pnpm exec markdown-table-formatter --check + # shellcheck disable=SC2086 + echo $ALL_CHANGED_FILES | tr ',' '\n' | pnpm exec markdown-table-formatter --check + env: + ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} diff --git a/.github/workflows/dogfood.yaml b/.github/workflows/dogfood.yaml index 6735f7d2ce..119cd4fe85 100644 --- a/.github/workflows/dogfood.yaml +++ b/.github/workflows/dogfood.yaml @@ -18,8 +18,7 @@ on: workflow_dispatch: permissions: - # Necessary for GCP authentication (https://github.com/google-github-actions/setup-gcloud#usage) - id-token: write + contents: read jobs: build_image: @@ -33,6 +32,8 @@ jobs: - name: Checkout uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false - name: Setup Nix uses: nixbuild/nix-quick-install-action@63ca48f939ee3b8d835f4126562537df0fee5b91 # v32 @@ -67,10 +68,11 @@ jobs: - name: "Branch name to Docker tag name" id: docker-tag-name run: | - tag=${{ steps.branch-name.outputs.current_branch }} # Replace / with --, e.g. user/feature => user--feature. - tag=${tag//\//--} - echo "tag=${tag}" >> $GITHUB_OUTPUT + tag=${BRANCH_NAME//\//--} + echo "tag=${tag}" >> "$GITHUB_OUTPUT" + env: + BRANCH_NAME: ${{ steps.branch-name.outputs.current_branch }} - name: Set up Depot CLI uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0 @@ -107,15 +109,20 @@ jobs: CURRENT_SYSTEM=$(nix eval --impure --raw --expr 'builtins.currentSystem') - docker image tag codercom/oss-dogfood-nix:latest-$CURRENT_SYSTEM codercom/oss-dogfood-nix:${{ steps.docker-tag-name.outputs.tag }} - docker image push codercom/oss-dogfood-nix:${{ steps.docker-tag-name.outputs.tag }} + docker image tag "codercom/oss-dogfood-nix:latest-$CURRENT_SYSTEM" "codercom/oss-dogfood-nix:${DOCKER_TAG}" + docker image push "codercom/oss-dogfood-nix:${DOCKER_TAG}" - docker image tag codercom/oss-dogfood-nix:latest-$CURRENT_SYSTEM codercom/oss-dogfood-nix:latest - docker image push codercom/oss-dogfood-nix:latest + docker image tag "codercom/oss-dogfood-nix:latest-$CURRENT_SYSTEM" "codercom/oss-dogfood-nix:latest" + docker image push "codercom/oss-dogfood-nix:latest" + env: + DOCKER_TAG: ${{ steps.docker-tag-name.outputs.tag }} deploy_template: needs: build_image runs-on: ubuntu-latest + permissions: + # Necessary for GCP authentication (https://github.com/google-github-actions/setup-gcloud#usage) + id-token: write steps: - name: Harden Runner uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 @@ -124,6 +131,8 @@ jobs: - name: Checkout uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false - name: Setup Terraform uses: ./.github/actions/setup-tf @@ -152,12 +161,12 @@ jobs: - name: Get short commit SHA if: github.ref == 'refs/heads/main' id: vars - run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + run: echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" - name: Get latest commit title if: github.ref == 'refs/heads/main' id: message - run: echo "pr_title=$(git log --format=%s -n 1 ${{ github.sha }})" >> $GITHUB_OUTPUT + run: echo "pr_title=$(git log --format=%s -n 1 ${{ github.sha }})" >> "$GITHUB_OUTPUT" - name: "Push template" if: github.ref == 'refs/heads/main' diff --git a/.github/workflows/nightly-gauntlet.yaml b/.github/workflows/nightly-gauntlet.yaml index 7bbf690f5e..5769b3b652 100644 --- a/.github/workflows/nightly-gauntlet.yaml +++ b/.github/workflows/nightly-gauntlet.yaml @@ -37,8 +37,8 @@ jobs: - name: Disable Spotlight Indexing if: runner.os == 'macOS' run: | - enabled=$(sudo mdutil -a -s | grep "Indexing enabled" | wc -l) - if [ $enabled -eq 0 ]; then + enabled=$(sudo mdutil -a -s | { grep -Fc "Indexing enabled" || true; }) + if [ "$enabled" -eq 0 ]; then echo "Spotlight indexing is already disabled" exit 0 fi @@ -50,12 +50,13 @@ jobs: # a separate repository to allow its use before actions/checkout. - name: Setup RAM Disks if: runner.os == 'Windows' - uses: coder/setup-ramdisk-action@e1100847ab2d7bcd9d14bcda8f2d1b0f07b36f1b + uses: coder/setup-ramdisk-action@e1100847ab2d7bcd9d14bcda8f2d1b0f07b36f1b # v0.1.0 - name: Checkout uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false - name: Setup Go uses: ./.github/actions/setup-go @@ -185,15 +186,15 @@ jobs: "fields": [ { "type": "mrkdwn", - "text": "*Workflow:*\n${{ github.workflow }}" + "text": "*Workflow:*\n'"${GITHUB_WORKFLOW}"'" }, { "type": "mrkdwn", - "text": "*Committer:*\n${{ github.actor }}" + "text": "*Committer:*\n'"${GITHUB_ACTOR}"'" }, { "type": "mrkdwn", - "text": "*Commit:*\n${{ github.sha }}" + "text": "*Commit:*\n'"${GITHUB_SHA}"'" } ] }, @@ -201,7 +202,7 @@ jobs: "type": "section", "text": { "type": "mrkdwn", - "text": "*View failure:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Click here>" + "text": "*View failure:* <'"${RUN_URL}"'|Click here>" } }, { @@ -212,4 +213,7 @@ jobs: } } ] - }' ${{ secrets.CI_FAILURE_SLACK_WEBHOOK }} + }' "${SLACK_WEBHOOK}" + env: + SLACK_WEBHOOK: ${{ secrets.CI_FAILURE_SLACK_WEBHOOK }} + RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" diff --git a/.github/workflows/pr-auto-assign.yaml b/.github/workflows/pr-auto-assign.yaml index 746b471f57..7e2f6441de 100644 --- a/.github/workflows/pr-auto-assign.yaml +++ b/.github/workflows/pr-auto-assign.yaml @@ -3,6 +3,7 @@ name: PR Auto Assign on: + # zizmor: ignore[dangerous-triggers] We explicitly want to run on pull_request_target. pull_request_target: types: [opened] diff --git a/.github/workflows/pr-cleanup.yaml b/.github/workflows/pr-cleanup.yaml index 4c3023990e..32e260b112 100644 --- a/.github/workflows/pr-cleanup.yaml +++ b/.github/workflows/pr-cleanup.yaml @@ -27,10 +27,12 @@ jobs: id: pr_number run: | if [ -n "${{ github.event.pull_request.number }}" ]; then - echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT + echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT" else - echo "PR_NUMBER=${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT + echo "PR_NUMBER=${PR_NUMBER}" >> "$GITHUB_OUTPUT" fi + env: + PR_NUMBER: ${{ github.event.inputs.pr_number }} - name: Delete image continue-on-error: true @@ -51,17 +53,21 @@ jobs: - name: Delete helm release run: | set -euo pipefail - helm delete --namespace "pr${{ steps.pr_number.outputs.PR_NUMBER }}" "pr${{ steps.pr_number.outputs.PR_NUMBER }}" || echo "helm release not found" + helm delete --namespace "pr${PR_NUMBER}" "pr${PR_NUMBER}" || echo "helm release not found" + env: + PR_NUMBER: ${{ steps.pr_number.outputs.PR_NUMBER }} - name: "Remove PR namespace" run: | - kubectl delete namespace "pr${{ steps.pr_number.outputs.PR_NUMBER }}" || echo "namespace not found" + kubectl delete namespace "pr${PR_NUMBER}" || echo "namespace not found" + env: + PR_NUMBER: ${{ steps.pr_number.outputs.PR_NUMBER }} - name: "Remove DNS records" run: | set -euo pipefail # Get identifier for the record - record_id=$(curl -X GET "https://api.cloudflare.com/client/v4/zones/${{ secrets.PR_DEPLOYMENTS_ZONE_ID }}/dns_records?name=%2A.pr${{ steps.pr_number.outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" \ + record_id=$(curl -X GET "https://api.cloudflare.com/client/v4/zones/${{ secrets.PR_DEPLOYMENTS_ZONE_ID }}/dns_records?name=%2A.pr${PR_NUMBER}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" \ -H "Authorization: Bearer ${{ secrets.PR_DEPLOYMENTS_CLOUDFLARE_API_TOKEN }}" \ -H "Content-Type:application/json" | jq -r '.result[0].id') || echo "DNS record not found" @@ -73,9 +79,13 @@ jobs: -H "Authorization: Bearer ${{ secrets.PR_DEPLOYMENTS_CLOUDFLARE_API_TOKEN }}" \ -H "Content-Type:application/json" | jq -r '.success' ) || echo "DNS record not found" + env: + PR_NUMBER: ${{ steps.pr_number.outputs.PR_NUMBER }} - name: "Delete certificate" if: ${{ github.event.pull_request.merged == true }} run: | set -euxo pipefail - kubectl delete certificate "pr${{ steps.pr_number.outputs.PR_NUMBER }}-tls" -n pr-deployment-certs || echo "certificate not found" + kubectl delete certificate "pr${PR_NUMBER}-tls" -n pr-deployment-certs || echo "certificate not found" + env: + PR_NUMBER: ${{ steps.pr_number.outputs.PR_NUMBER }} diff --git a/.github/workflows/pr-deploy.yaml b/.github/workflows/pr-deploy.yaml index e31cc26e79..ccf7511eaf 100644 --- a/.github/workflows/pr-deploy.yaml +++ b/.github/workflows/pr-deploy.yaml @@ -45,6 +45,8 @@ jobs: - name: Checkout uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false - name: Check if PR is open id: check_pr @@ -55,7 +57,7 @@ jobs: echo "PR doesn't exist or is closed." pr_open=false fi - echo "pr_open=$pr_open" >> $GITHUB_OUTPUT + echo "pr_open=$pr_open" >> "$GITHUB_OUTPUT" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -82,6 +84,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false - name: Get PR number, title, and branch name id: pr_info @@ -90,9 +93,11 @@ jobs: PR_NUMBER=$(gh pr view --json number | jq -r '.number') PR_TITLE=$(gh pr view --json title | jq -r '.title') PR_URL=$(gh pr view --json url | jq -r '.url') - echo "PR_URL=$PR_URL" >> $GITHUB_OUTPUT - echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT - echo "PR_TITLE=$PR_TITLE" >> $GITHUB_OUTPUT + { + echo "PR_URL=$PR_URL" + echo "PR_NUMBER=$PR_NUMBER" + echo "PR_TITLE=$PR_TITLE" + } >> "$GITHUB_OUTPUT" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -100,8 +105,8 @@ jobs: id: set_tags run: | set -euo pipefail - echo "CODER_BASE_IMAGE_TAG=$CODER_BASE_IMAGE_TAG" >> $GITHUB_OUTPUT - echo "CODER_IMAGE_TAG=$CODER_IMAGE_TAG" >> $GITHUB_OUTPUT + echo "CODER_BASE_IMAGE_TAG=$CODER_BASE_IMAGE_TAG" >> "$GITHUB_OUTPUT" + echo "CODER_IMAGE_TAG=$CODER_IMAGE_TAG" >> "$GITHUB_OUTPUT" env: CODER_BASE_IMAGE_TAG: ghcr.io/coder/coder-preview-base:pr${{ steps.pr_info.outputs.PR_NUMBER }} CODER_IMAGE_TAG: ghcr.io/coder/coder-preview:pr${{ steps.pr_info.outputs.PR_NUMBER }} @@ -118,14 +123,16 @@ jobs: id: check_deployment run: | set -euo pipefail - if helm status "pr${{ steps.pr_info.outputs.PR_NUMBER }}" --namespace "pr${{ steps.pr_info.outputs.PR_NUMBER }}" > /dev/null 2>&1; then + if helm status "pr${PR_NUMBER}" --namespace "pr${PR_NUMBER}" > /dev/null 2>&1; then echo "Deployment already exists. Skipping deployment." NEW=false else echo "Deployment doesn't exist." NEW=true fi - echo "NEW=$NEW" >> $GITHUB_OUTPUT + echo "NEW=$NEW" >> "$GITHUB_OUTPUT" + env: + PR_NUMBER: ${{ steps.pr_info.outputs.PR_NUMBER }} - name: Check changed files uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 @@ -154,17 +161,20 @@ jobs: - name: Print number of changed files run: | set -euo pipefail - echo "Total number of changed files: ${{ steps.filter.outputs.all_count }}" - echo "Number of ignored files: ${{ steps.filter.outputs.ignored_count }}" + echo "Total number of changed files: ${ALL_COUNT}" + echo "Number of ignored files: ${IGNORED_COUNT}" + env: + ALL_COUNT: ${{ steps.filter.outputs.all_count }} + IGNORED_COUNT: ${{ steps.filter.outputs.ignored_count }} - name: Build conditionals id: build_conditionals run: | set -euo pipefail # build if the workflow is manually triggered and the deployment doesn't exist (first build or force rebuild) - echo "first_or_force_build=${{ (github.event_name == 'workflow_dispatch' && steps.check_deployment.outputs.NEW == 'true') || github.event.inputs.build == 'true' }}" >> $GITHUB_OUTPUT + echo "first_or_force_build=${{ (github.event_name == 'workflow_dispatch' && steps.check_deployment.outputs.NEW == 'true') || github.event.inputs.build == 'true' }}" >> "$GITHUB_OUTPUT" # build if the deployment already exist and there are changes in the files that we care about (automatic updates) - echo "automatic_rebuild=${{ steps.check_deployment.outputs.NEW == 'false' && steps.filter.outputs.all_count > steps.filter.outputs.ignored_count }}" >> $GITHUB_OUTPUT + echo "automatic_rebuild=${{ steps.check_deployment.outputs.NEW == 'false' && steps.filter.outputs.all_count > steps.filter.outputs.ignored_count }}" >> "$GITHUB_OUTPUT" comment-pr: needs: get_info @@ -226,6 +236,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false - name: Setup Node uses: ./.github/actions/setup-node @@ -250,12 +261,13 @@ jobs: make gen/mark-fresh export DOCKER_IMAGE_NO_PREREQUISITES=true version="$(./scripts/version.sh)" - export CODER_IMAGE_BUILD_BASE_TAG="$(CODER_IMAGE_BASE=coder-base ./scripts/image_tag.sh --version "$version")" + CODER_IMAGE_BUILD_BASE_TAG="$(CODER_IMAGE_BASE=coder-base ./scripts/image_tag.sh --version "$version")" + export CODER_IMAGE_BUILD_BASE_TAG make -j build/coder_linux_amd64 ./scripts/build_docker.sh \ --arch amd64 \ - --target ${{ env.CODER_IMAGE_TAG }} \ - --version $version \ + --target "${CODER_IMAGE_TAG}" \ + --version "$version" \ --push \ build/coder_linux_amd64 @@ -293,13 +305,13 @@ jobs: set -euo pipefail foundTag=$( gh api /orgs/coder/packages/container/coder-preview/versions | - jq -r --arg tag "pr${{ env.PR_NUMBER }}" '.[] | + jq -r --arg tag "pr${PR_NUMBER}" '.[] | select(.metadata.container.tags == [$tag]) | .metadata.container.tags[0]' ) if [ -z "$foundTag" ]; then echo "Image not found" - echo "${{ env.CODER_IMAGE_TAG }} not found in ghcr.io/coder/coder-preview" + echo "${CODER_IMAGE_TAG} not found in ghcr.io/coder/coder-preview" exit 1 else echo "Image found" @@ -314,40 +326,42 @@ jobs: curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.PR_DEPLOYMENTS_ZONE_ID }}/dns_records" \ -H "Authorization: Bearer ${{ secrets.PR_DEPLOYMENTS_CLOUDFLARE_API_TOKEN }}" \ -H "Content-Type:application/json" \ - --data '{"type":"CNAME","name":"*.${{ env.PR_HOSTNAME }}","content":"${{ env.PR_HOSTNAME }}","ttl":1,"proxied":false}' + --data '{"type":"CNAME","name":"*.'"${PR_HOSTNAME}"'","content":"'"${PR_HOSTNAME}"'","ttl":1,"proxied":false}' - name: Create PR namespace if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' run: | set -euo pipefail # try to delete the namespace, but don't fail if it doesn't exist - kubectl delete namespace "pr${{ env.PR_NUMBER }}" || true - kubectl create namespace "pr${{ env.PR_NUMBER }}" + kubectl delete namespace "pr${PR_NUMBER}" || true + kubectl create namespace "pr${PR_NUMBER}" - name: Checkout uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false - name: Check and Create Certificate if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' run: | # Using kubectl to check if a Certificate resource already exists # we are doing this to avoid letsenrypt rate limits - if ! kubectl get certificate pr${{ env.PR_NUMBER }}-tls -n pr-deployment-certs > /dev/null 2>&1; then + if ! kubectl get certificate "pr${PR_NUMBER}-tls" -n pr-deployment-certs > /dev/null 2>&1; then echo "Certificate doesn't exist. Creating a new one." envsubst < ./.github/pr-deployments/certificate.yaml | kubectl apply -f - else echo "Certificate exists. Skipping certificate creation." fi - echo "Copy certificate from pr-deployment-certs to pr${{ env.PR_NUMBER }} namespace" - until kubectl get secret pr${{ env.PR_NUMBER }}-tls -n pr-deployment-certs &> /dev/null + echo "Copy certificate from pr-deployment-certs to pr${PR_NUMBER} namespace" + until kubectl get secret "pr${PR_NUMBER}-tls" -n pr-deployment-certs &> /dev/null do - echo "Waiting for secret pr${{ env.PR_NUMBER }}-tls to be created..." + echo "Waiting for secret pr${PR_NUMBER}-tls to be created..." sleep 5 done ( - kubectl get secret pr${{ env.PR_NUMBER }}-tls -n pr-deployment-certs -o json | + kubectl get secret "pr${PR_NUMBER}-tls" -n pr-deployment-certs -o json | jq 'del(.metadata.namespace,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.selfLink,.metadata.uid,.metadata.managedFields)' | - kubectl -n pr${{ env.PR_NUMBER }} apply -f - + kubectl -n "pr${PR_NUMBER}" apply -f - ) - name: Set up PostgreSQL database @@ -355,13 +369,13 @@ jobs: run: | helm repo add bitnami https://charts.bitnami.com/bitnami helm install coder-db bitnami/postgresql \ - --namespace pr${{ env.PR_NUMBER }} \ + --namespace "pr${PR_NUMBER}" \ --set auth.username=coder \ --set auth.password=coder \ --set auth.database=coder \ --set persistence.size=10Gi - kubectl create secret generic coder-db-url -n pr${{ env.PR_NUMBER }} \ - --from-literal=url="postgres://coder:coder@coder-db-postgresql.pr${{ env.PR_NUMBER }}.svc.cluster.local:5432/coder?sslmode=disable" + kubectl create secret generic coder-db-url -n "pr${PR_NUMBER}" \ + --from-literal=url="postgres://coder:coder@coder-db-postgresql.pr${PR_NUMBER}.svc.cluster.local:5432/coder?sslmode=disable" - name: Create a service account, role, and rolebinding for the PR namespace if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' @@ -383,8 +397,8 @@ jobs: run: | set -euo pipefail helm dependency update --skip-refresh ./helm/coder - helm upgrade --install "pr${{ env.PR_NUMBER }}" ./helm/coder \ - --namespace "pr${{ env.PR_NUMBER }}" \ + helm upgrade --install "pr${PR_NUMBER}" ./helm/coder \ + --namespace "pr${PR_NUMBER}" \ --values ./pr-deploy-values.yaml \ --force @@ -393,8 +407,8 @@ jobs: run: | helm repo add coder-logstream-kube https://helm.coder.com/logstream-kube helm upgrade --install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \ - --namespace "pr${{ env.PR_NUMBER }}" \ - --set url="https://${{ env.PR_HOSTNAME }}" + --namespace "pr${PR_NUMBER}" \ + --set url="https://${PR_HOSTNAME}" - name: Get Coder binary if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' @@ -402,16 +416,16 @@ jobs: set -euo pipefail DEST="${HOME}/coder" - URL="https://${{ env.PR_HOSTNAME }}/bin/coder-linux-amd64" + URL="https://${PR_HOSTNAME}/bin/coder-linux-amd64" - mkdir -p "$(dirname ${DEST})" + mkdir -p "$(dirname "$DEST")" COUNT=0 - until $(curl --output /dev/null --silent --head --fail "$URL"); do + until curl --output /dev/null --silent --head --fail "$URL"; do printf '.' sleep 5 COUNT=$((COUNT+1)) - if [ $COUNT -ge 60 ]; then + if [ "$COUNT" -ge 60 ]; then echo "Timed out waiting for URL to be available" exit 1 fi @@ -435,24 +449,24 @@ jobs: # add mask so that the password is not printed to the logs echo "::add-mask::$password" - echo "password=$password" >> $GITHUB_OUTPUT + echo "password=$password" >> "$GITHUB_OUTPUT" coder login \ - --first-user-username pr${{ env.PR_NUMBER }}-admin \ - --first-user-email pr${{ env.PR_NUMBER }}@coder.com \ - --first-user-password $password \ + --first-user-username "pr${PR_NUMBER}-admin" \ + --first-user-email "pr${PR_NUMBER}@coder.com" \ + --first-user-password "$password" \ --first-user-trial=false \ --use-token-as-session \ - https://${{ env.PR_HOSTNAME }} + "https://${PR_HOSTNAME}" # Create a user for the github.actor # TODO: update once https://github.com/coder/coder/issues/15466 is resolved # coder users create \ - # --username ${{ github.actor }} \ + # --username ${GITHUB_ACTOR} \ # --login-type github # promote the user to admin role - # coder org members edit-role ${{ github.actor }} organization-admin + # coder org members edit-role ${GITHUB_ACTOR} organization-admin # TODO: update once https://github.com/coder/internal/issues/207 is resolved - name: Send Slack notification @@ -461,17 +475,19 @@ jobs: curl -s -o /dev/null -X POST -H 'Content-type: application/json' \ -d \ '{ - "pr_number": "'"${{ env.PR_NUMBER }}"'", - "pr_url": "'"${{ env.PR_URL }}"'", - "pr_title": "'"${{ env.PR_TITLE }}"'", - "pr_access_url": "'"https://${{ env.PR_HOSTNAME }}"'", - "pr_username": "'"pr${{ env.PR_NUMBER }}-admin"'", - "pr_email": "'"pr${{ env.PR_NUMBER }}@coder.com"'", - "pr_password": "'"${{ steps.setup_deployment.outputs.password }}"'", - "pr_actor": "'"${{ github.actor }}"'" + "pr_number": "'"${PR_NUMBER}"'", + "pr_url": "'"${PR_URL}"'", + "pr_title": "'"${PR_TITLE}"'", + "pr_access_url": "'"https://${PR_HOSTNAME}"'", + "pr_username": "'"pr${PR_NUMBER}-admin"'", + "pr_email": "'"pr${PR_NUMBER}@coder.com"'", + "pr_password": "'"${PASSWORD}"'", + "pr_actor": "'"${GITHUB_ACTOR}"'" }' \ ${{ secrets.PR_DEPLOYMENTS_SLACK_WEBHOOK }} echo "Slack notification sent" + env: + PASSWORD: ${{ steps.setup_deployment.outputs.password }} - name: Find Comment uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0 @@ -504,7 +520,7 @@ jobs: run: | set -euo pipefail cd .github/pr-deployments/template - coder templates push -y --variable namespace=pr${{ env.PR_NUMBER }} kubernetes + coder templates push -y --variable "namespace=pr${PR_NUMBER}" kubernetes # Create workspace coder create --template="kubernetes" kube --parameter cpu=2 --parameter memory=4 --parameter home_disk_size=2 -y diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 06041e1865..f4f9c8f317 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -68,6 +68,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false # If the event that triggered the build was an annotated tag (which our # tags are supposed to be), actions/checkout has a bug where the tag in @@ -80,9 +81,11 @@ jobs: - name: Setup build tools run: | brew install bash gnu-getopt make - echo "$(brew --prefix bash)/bin" >> $GITHUB_PATH - echo "$(brew --prefix gnu-getopt)/bin" >> $GITHUB_PATH - echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH + { + echo "$(brew --prefix bash)/bin" + echo "$(brew --prefix gnu-getopt)/bin" + echo "$(brew --prefix make)/libexec/gnubin" + } >> "$GITHUB_PATH" - name: Switch XCode Version uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 @@ -169,6 +172,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false # If the event that triggered the build was an annotated tag (which our # tags are supposed to be), actions/checkout has a bug where the tag in @@ -183,9 +187,9 @@ jobs: run: | set -euo pipefail version="$(./scripts/version.sh)" - echo "version=$version" >> $GITHUB_OUTPUT + echo "version=$version" >> "$GITHUB_OUTPUT" # Speed up future version.sh calls. - echo "CODER_FORCE_VERSION=$version" >> $GITHUB_ENV + echo "CODER_FORCE_VERSION=$version" >> "$GITHUB_ENV" echo "$version" # Verify that all expectations for a release are met. @@ -227,7 +231,7 @@ jobs: release_notes_file="$(mktemp -t release_notes.XXXXXX)" echo "$CODER_RELEASE_NOTES" > "$release_notes_file" - echo CODER_RELEASE_NOTES_FILE="$release_notes_file" >> $GITHUB_ENV + echo CODER_RELEASE_NOTES_FILE="$release_notes_file" >> "$GITHUB_ENV" - name: Show release notes run: | @@ -377,9 +381,9 @@ jobs: set -euo pipefail if [[ "${CODER_RELEASE:-}" != *t* ]] || [[ "${CODER_DRY_RUN:-}" == *t* ]]; then # Empty value means use the default and avoid building a fresh one. - echo "tag=" >> $GITHUB_OUTPUT + echo "tag=" >> "$GITHUB_OUTPUT" else - echo "tag=$(CODER_IMAGE_BASE=ghcr.io/coder/coder-base ./scripts/image_tag.sh)" >> $GITHUB_OUTPUT + echo "tag=$(CODER_IMAGE_BASE=ghcr.io/coder/coder-base ./scripts/image_tag.sh)" >> "$GITHUB_OUTPUT" fi - name: Create empty base-build-context directory @@ -414,7 +418,7 @@ jobs: # available immediately for i in {1..10}; do rc=0 - raw_manifests=$(docker buildx imagetools inspect --raw "${{ steps.image-base-tag.outputs.tag }}") || rc=$? + raw_manifests=$(docker buildx imagetools inspect --raw "${IMAGE_TAG}") || rc=$? if [[ "$rc" -eq 0 ]]; then break fi @@ -436,6 +440,8 @@ jobs: echo "$manifests" | grep -q linux/amd64 echo "$manifests" | grep -q linux/arm64 echo "$manifests" | grep -q linux/arm/v7 + env: + IMAGE_TAG: ${{ steps.image-base-tag.outputs.tag }} # GitHub attestation provides SLSA provenance for Docker images, establishing a verifiable # record that these images were built in GitHub Actions with specific inputs and environment. @@ -503,7 +509,7 @@ jobs: # Save multiarch image tag for attestation multiarch_image="$(./scripts/image_tag.sh)" - echo "multiarch_image=${multiarch_image}" >> $GITHUB_OUTPUT + echo "multiarch_image=${multiarch_image}" >> "$GITHUB_OUTPUT" # For debugging, print all docker image tags docker images @@ -511,16 +517,15 @@ jobs: # if the current version is equal to the highest (according to semver) # version in the repo, also create a multi-arch image as ":latest" and # push it - created_latest_tag=false if [[ "$(git tag | grep '^v' | grep -vE '(rc|dev|-|\+|\/)' | sort -r --version-sort | head -n1)" == "v$(./scripts/version.sh)" ]]; then + # shellcheck disable=SC2046 ./scripts/build_docker_multiarch.sh \ --push \ --target "$(./scripts/image_tag.sh --version latest)" \ $(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag) - created_latest_tag=true - echo "created_latest_tag=true" >> $GITHUB_OUTPUT + echo "created_latest_tag=true" >> "$GITHUB_OUTPUT" else - echo "created_latest_tag=false" >> $GITHUB_OUTPUT + echo "created_latest_tag=false" >> "$GITHUB_OUTPUT" fi env: CODER_BASE_IMAGE_TAG: ${{ steps.image-base-tag.outputs.tag }} @@ -528,24 +533,27 @@ jobs: - name: SBOM Generation and Attestation if: ${{ !inputs.dry_run }} env: - COSIGN_EXPERIMENTAL: "1" + COSIGN_EXPERIMENTAL: '1' + MULTIARCH_IMAGE: ${{ steps.build_docker.outputs.multiarch_image }} + VERSION: ${{ steps.version.outputs.version }} + CREATED_LATEST_TAG: ${{ steps.build_docker.outputs.created_latest_tag }} run: | set -euxo pipefail # Generate SBOM for multi-arch image with version in filename - echo "Generating SBOM for multi-arch image: ${{ steps.build_docker.outputs.multiarch_image }}" - syft "${{ steps.build_docker.outputs.multiarch_image }}" -o spdx-json > coder_${{ steps.version.outputs.version }}_sbom.spdx.json + echo "Generating SBOM for multi-arch image: ${MULTIARCH_IMAGE}" + syft "${MULTIARCH_IMAGE}" -o spdx-json > "coder_${VERSION}_sbom.spdx.json" # Attest SBOM to multi-arch image - echo "Attesting SBOM to multi-arch image: ${{ steps.build_docker.outputs.multiarch_image }}" - cosign clean --force=true "${{ steps.build_docker.outputs.multiarch_image }}" + echo "Attesting SBOM to multi-arch image: ${MULTIARCH_IMAGE}" + cosign clean --force=true "${MULTIARCH_IMAGE}" cosign attest --type spdxjson \ - --predicate coder_${{ steps.version.outputs.version }}_sbom.spdx.json \ + --predicate "coder_${VERSION}_sbom.spdx.json" \ --yes \ - "${{ steps.build_docker.outputs.multiarch_image }}" + "${MULTIARCH_IMAGE}" # If latest tag was created, also attest it - if [[ "${{ steps.build_docker.outputs.created_latest_tag }}" == "true" ]]; then + if [[ "${CREATED_LATEST_TAG}" == "true" ]]; then latest_tag="$(./scripts/image_tag.sh --version latest)" echo "Generating SBOM for latest image: ${latest_tag}" syft "${latest_tag}" -o spdx-json > coder_latest_sbom.spdx.json @@ -599,7 +607,7 @@ jobs: - name: Get latest tag name id: latest_tag if: ${{ !inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' }} - run: echo "tag=$(./scripts/image_tag.sh --version latest)" >> $GITHUB_OUTPUT + run: echo "tag=$(./scripts/image_tag.sh --version latest)" >> "$GITHUB_OUTPUT" # If this is the highest version according to semver, also attest the "latest" tag - name: GitHub Attestation for "latest" Docker image @@ -642,7 +650,7 @@ jobs: # Report attestation failures but don't fail the workflow - name: Check attestation status if: ${{ !inputs.dry_run }} - run: | + run: | # zizmor: ignore[template-injection] We're just reading steps.attest_x.outcome here, no risk of injection if [[ "${{ steps.attest_base.outcome }}" == "failure" && "${{ steps.attest_base.conclusion }}" != "skipped" ]]; then echo "::warning::GitHub attestation for base image failed" fi @@ -707,11 +715,11 @@ jobs: ./build/*.apk ./build/*.deb ./build/*.rpm - ./coder_${{ steps.version.outputs.version }}_sbom.spdx.json + "./coder_${VERSION}_sbom.spdx.json" ) # Only include the latest SBOM file if it was created - if [[ "${{ steps.build_docker.outputs.created_latest_tag }}" == "true" ]]; then + if [[ "${CREATED_LATEST_TAG}" == "true" ]]; then files+=(./coder_latest_sbom.spdx.json) fi @@ -722,6 +730,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }} + VERSION: ${{ steps.version.outputs.version }} + CREATED_LATEST_TAG: ${{ steps.build_docker.outputs.created_latest_tag }} - name: Authenticate to Google Cloud uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12 @@ -742,12 +752,12 @@ jobs: cp "build/provisioner_helm_${version}.tgz" build/helm gsutil cp gs://helm.coder.com/v2/index.yaml build/helm/index.yaml helm repo index build/helm --url https://helm.coder.com/v2 --merge build/helm/index.yaml - gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/coder_helm_${version}.tgz gs://helm.coder.com/v2 - gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/provisioner_helm_${version}.tgz gs://helm.coder.com/v2 - gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/index.yaml gs://helm.coder.com/v2 - gsutil -h "Cache-Control:no-cache,max-age=0" cp helm/artifacthub-repo.yml gs://helm.coder.com/v2 - helm push build/coder_helm_${version}.tgz oci://ghcr.io/coder/chart - helm push build/provisioner_helm_${version}.tgz oci://ghcr.io/coder/chart + gsutil -h "Cache-Control:no-cache,max-age=0" cp "build/helm/coder_helm_${version}.tgz" gs://helm.coder.com/v2 + gsutil -h "Cache-Control:no-cache,max-age=0" cp "build/helm/provisioner_helm_${version}.tgz" gs://helm.coder.com/v2 + gsutil -h "Cache-Control:no-cache,max-age=0" cp "build/helm/index.yaml" gs://helm.coder.com/v2 + gsutil -h "Cache-Control:no-cache,max-age=0" cp "helm/artifacthub-repo.yml" gs://helm.coder.com/v2 + helm push "build/coder_helm_${version}.tgz" oci://ghcr.io/coder/chart + helm push "build/provisioner_helm_${version}.tgz" oci://ghcr.io/coder/chart - name: Upload artifacts to actions (if dry-run) if: ${{ inputs.dry_run }} @@ -798,12 +808,12 @@ jobs: - name: Update homebrew env: - # Variables used by the `gh` command GH_REPO: coder/homebrew-coder GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} + VERSION: ${{ needs.release.outputs.version }} run: | # Keep version number around for reference, removing any potential leading v - coder_version="$(echo "${{ needs.release.outputs.version }}" | tr -d v)" + coder_version="$(echo "${VERSION}" | tr -d v)" set -euxo pipefail @@ -822,9 +832,9 @@ jobs: wget "$checksums_url" -O checksums.txt # Get the SHAs - darwin_arm_sha="$(cat checksums.txt | grep "darwin_arm64.zip" | awk '{ print $1 }')" - darwin_intel_sha="$(cat checksums.txt | grep "darwin_amd64.zip" | awk '{ print $1 }')" - linux_sha="$(cat checksums.txt | grep "linux_amd64.tar.gz" | awk '{ print $1 }')" + darwin_arm_sha="$(grep "darwin_arm64.zip" checksums.txt | awk '{ print $1 }')" + darwin_intel_sha="$(grep "darwin_amd64.zip" checksums.txt | awk '{ print $1 }')" + linux_sha="$(grep "linux_amd64.tar.gz" checksums.txt | awk '{ print $1 }')" echo "macOS arm64: $darwin_arm_sha" echo "macOS amd64: $darwin_intel_sha" @@ -837,7 +847,7 @@ jobs: # Check if a PR already exists. pr_count="$(gh pr list --search "head:$brew_branch" --json id,closed | jq -r ".[] | select(.closed == false) | .id" | wc -l)" - if [[ "$pr_count" > 0 ]]; then + if [ "$pr_count" -gt 0 ]; then echo "Bailing out as PR already exists" 2>&1 exit 0 fi @@ -856,8 +866,8 @@ jobs: -B master -H "$brew_branch" \ -t "coder $coder_version" \ -b "" \ - -r "${{ github.actor }}" \ - -a "${{ github.actor }}" \ + -r "${GITHUB_ACTOR}" \ + -a "${GITHUB_ACTOR}" \ -b "This automatic PR was triggered by the release of Coder v$coder_version" publish-winget: @@ -881,6 +891,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false # If the event that triggered the build was an annotated tag (which our # tags are supposed to be), actions/checkout has a bug where the tag in @@ -899,7 +910,7 @@ jobs: # The package version is the same as the tag minus the leading "v". # The version in this output already has the leading "v" removed but # we do it again to be safe. - $version = "${{ needs.release.outputs.version }}".Trim('v') + $version = $env:VERSION.Trim('v') $release_assets = gh release view --repo coder/coder "v${version}" --json assets | ` ConvertFrom-Json @@ -931,13 +942,14 @@ jobs: # For wingetcreate. We need a real token since we're pushing a commit # to GitHub and then making a PR in a different repo. WINGET_GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} + VERSION: ${{ needs.release.outputs.version }} - name: Comment on PR run: | # wait 30 seconds Start-Sleep -Seconds 30.0 # Find the PR that wingetcreate just made. - $version = "${{ needs.release.outputs.version }}".Trim('v') + $version = $env:VERSION.Trim('v') $pr_list = gh pr list --repo microsoft/winget-pkgs --search "author:cdrci Coder.Coder version ${version}" --limit 1 --json number | ` ConvertFrom-Json $pr_number = $pr_list[0].number @@ -948,6 +960,7 @@ jobs: # For gh CLI. We need a real token since we're commenting on a PR in a # different repo. GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} + VERSION: ${{ needs.release.outputs.version }} # publish-sqlc pushes the latest schema to sqlc cloud. # At present these pushes cannot be tagged, so the last push is always the latest. @@ -966,6 +979,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 1 + persist-credentials: false # We need golang to run the migration main.go - name: Setup Go diff --git a/.github/workflows/security.yaml b/.github/workflows/security.yaml index 27b5137738..e7fde82bf1 100644 --- a/.github/workflows/security.yaml +++ b/.github/workflows/security.yaml @@ -33,6 +33,8 @@ jobs: - name: Checkout uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false - name: Setup Go uses: ./.github/actions/setup-go @@ -75,6 +77,7 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: fetch-depth: 0 + persist-credentials: false - name: Setup Go uses: ./.github/actions/setup-go @@ -134,12 +137,13 @@ jobs: # This environment variables forces scripts/build_docker.sh to build # the base image tag locally instead of using the cached version from # the registry. - export CODER_IMAGE_BUILD_BASE_TAG="$(CODER_IMAGE_BASE=coder-base ./scripts/image_tag.sh --version "$version")" + CODER_IMAGE_BUILD_BASE_TAG="$(CODER_IMAGE_BASE=coder-base ./scripts/image_tag.sh --version "$version")" + export CODER_IMAGE_BUILD_BASE_TAG # We would like to use make -j here, but it doesn't work with the some recent additions # to our code generation. make "$image_job" - echo "image=$(cat "$image_job")" >> $GITHUB_OUTPUT + echo "image=$(cat "$image_job")" >> "$GITHUB_OUTPUT" - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index c0c2494db6..27ec157fa0 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -102,6 +102,8 @@ jobs: - name: Checkout repository uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false - name: Run delete-old-branches-action uses: beatlabs/delete-old-branches-action@4eeeb8740ff8b3cb310296ddd6b43c3387734588 # v0.0.11 with: diff --git a/.github/workflows/weekly-docs.yaml b/.github/workflows/weekly-docs.yaml index 8d152f7398..56f5e79930 100644 --- a/.github/workflows/weekly-docs.yaml +++ b/.github/workflows/weekly-docs.yaml @@ -27,6 +27,8 @@ jobs: - name: Checkout uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false - name: Check Markdown links uses: umbrelladocs/action-linkspector@874d01cae9fd488e3077b08952093235bd626977 # v1.3.7 @@ -41,7 +43,10 @@ jobs: - name: Send Slack notification if: failure() && github.event_name == 'schedule' run: | - curl -X POST -H 'Content-type: application/json' -d '{"msg":"Broken links found in the documentation. Please check the logs at ${{ env.LOGS_URL }}"}' ${{ secrets.DOCS_LINK_SLACK_WEBHOOK }} + curl \ + -X POST \ + -H 'Content-type: application/json' \ + -d '{"msg":"Broken links found in the documentation. Please check the logs at '"${LOGS_URL}"'"}' "${{ secrets.DOCS_LINK_SLACK_WEBHOOK }}" echo "Sent Slack notification" env: LOGS_URL: https://github.com/coder/coder/actions/runs/${{ github.run_id }} diff --git a/Makefile b/Makefile index a5341ee79f..e72a1f7b62 100644 --- a/Makefile +++ b/Makefile @@ -559,7 +559,9 @@ else endif .PHONY: fmt/markdown -lint: lint/shellcheck lint/go lint/ts lint/examples lint/helm lint/site-icons lint/markdown +# Note: we don't run zizmor in the lint target because it takes a while. CI +# runs it explicitly. +lint: lint/shellcheck lint/go lint/ts lint/examples lint/helm lint/site-icons lint/markdown lint/actions/actionlint .PHONY: lint lint/site-icons: @@ -598,6 +600,20 @@ lint/markdown: node_modules/.installed pnpm lint-docs .PHONY: lint/markdown +lint/actions: lint/actions/actionlint lint/actions/zizmor +.PHONY: lint/actions + +lint/actions/actionlint: + go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.7 +.PHONY: lint/actions/actionlint + +lint/actions/zizmor: + ./scripts/zizmor.sh \ + --strict-collection \ + --persona=regular \ + . +.PHONY: lint/actions/zizmor + # All files generated by the database should be added here, and this can be used # as a target for jobs that need to run after the database is generated. DB_GEN_FILES := \ diff --git a/docs/tutorials/testing-templates.md b/docs/tutorials/testing-templates.md index bcfa33a74e..025c0d6ace 100644 --- a/docs/tutorials/testing-templates.md +++ b/docs/tutorials/testing-templates.md @@ -86,7 +86,7 @@ jobs: - name: Get short commit SHA to use as template version name id: name - run: echo "version_name=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + run: echo "version_name=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" - name: Get latest commit title to use as template version description id: message diff --git a/scripts/zizmor.sh b/scripts/zizmor.sh new file mode 100755 index 0000000000..a9326e2ee0 --- /dev/null +++ b/scripts/zizmor.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# Usage: ./zizmor.sh [args...] +# +# This script is a wrapper around the zizmor Docker image. Zizmor lints GitHub +# actions workflows. +# +# We use Docker to run zizmor since it's written in Rust and is difficult to +# install on Ubuntu runners without building it with a Rust toolchain, which +# takes a long time. +# +# The repo is mounted at /repo and the working directory is set to /repo. + +set -euo pipefail +# shellcheck source=scripts/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" + +cdroot + +image_tag="ghcr.io/zizmorcore/zizmor:1.11.0" +docker_args=( + "--rm" + "--volume" "$(pwd):/repo" + "--workdir" "/repo" + "--network" "host" +) + +if [[ -t 0 ]]; then + docker_args+=("-it") +fi + +# If no GH_TOKEN is set, try to get one from `gh auth token`. +if [[ "${GH_TOKEN:-}" == "" ]] && command -v gh &>/dev/null; then + set +e + GH_TOKEN="$(gh auth token)" + export GH_TOKEN + set -e +fi + +# Pass through the GitHub token if it's set, which allows zizmor to scan +# imported workflows too. +if [[ "${GH_TOKEN:-}" != "" ]]; then + docker_args+=("--env" "GH_TOKEN") +fi + +logrun exec docker run "${docker_args[@]}" "$image_tag" "$@"