Skip to content

Fix every false positive, false negative, warning and error in CI/CD #38

Fix every false positive, false negative, warning and error in CI/CD

Fix every false positive, false negative, warning and error in CI/CD #38

Workflow file for this run

name: go
on:
push:
branches: main
paths:
- 'go/**'
- '.github/workflows/go.yml'
pull_request:
paths:
- 'go/**'
- '.github/workflows/go.yml'
workflow_dispatch:
inputs:
verbose:
description: 'Print extra diagnostics (never secret values)'
type: boolean
required: false
default: false
permissions:
contents: read
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CI_VERBOSE: ${{ inputs.verbose && 'true' || vars.CI_VERBOSE || 'false' }}
defaults:
run:
working-directory: go
jobs:
findChangedGoFiles:
runs-on: ubuntu-latest
timeout-minutes: 10
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-detect-changes
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
outputs:
isGoFilesChanged: ${{ steps.setIsGoFilesChangedOutput.outputs.isGoFilesChanged }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 0
- name: Get changed files using defaults
id: changed-files
uses: tj-actions/changed-files@v47
- name: Set output isGoFilesChanged
id: setIsGoFilesChangedOutput
env:
ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
run: |
isGoFilesChanged='false'
# workflow_dispatch has no diff to inspect, so treat it as "changed".
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
isGoFilesChanged='true'
fi
echo "Changed files: $ALL_CHANGED_FILES"
for changedFile in $ALL_CHANGED_FILES; do
if [[ $changedFile == go/* ]] || [[ $changedFile == .github/workflows/go.yml ]]; then
isGoFilesChanged='true'
break
fi
done
echo "isGoFilesChanged=${isGoFilesChanged}" >> "$GITHUB_OUTPUT"
echo "isGoFilesChanged: ${isGoFilesChanged}"
lint:
needs: [findChangedGoFiles]
if: ${{ needs.findChangedGoFiles.outputs.isGoFilesChanged == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 10
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-lint
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
submodules: true
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: '1.21'
# This module has no dependencies: go/go.sum does not exist and
# go.mod has no require block. Pointing the dependency cache at a
# file that is not there made setup-go emit "Restore cache failed:
# Some specified paths were not resolved" on every run, in both
# jobs. There is nothing to cache, so the cache is off.
cache: false
- name: Check formatting
run: |
gofmt_output=$(gofmt -l .)
if [ -n "$gofmt_output" ]; then
echo "The following files are not formatted correctly:"
echo "$gofmt_output"
exit 1
fi
- name: Run go vet
run: go vet ./...
test:
needs: [findChangedGoFiles, lint]
if: ${{ needs.findChangedGoFiles.outputs.isGoFilesChanged == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 20
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-test
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
env:
# A step `if:` cannot read the secrets context, so the presence of the
# token is exposed as job-level env for the coverage step below.
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
submodules: true
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: '1.21'
# This module has no dependencies: go/go.sum does not exist and
# go.mod has no require block. Pointing the dependency cache at a
# file that is not there made setup-go emit "Restore cache failed:
# Some specified paths were not resolved" on every run, in both
# jobs. There is nothing to cache, so the cache is off.
cache: false
- name: Build
run: go build -v ./...
- name: Test
run: go test -v -race -coverprofile=coverage.out ./...
# Codecov requires a token, and this repository has no CODECOV_TOKEN:
# every run answered `Upload queued for processing failed: {"message":
# "Token required - not valid tokenless upload"}` and the step still
# reported success, because fail_ci_if_error was false. The job has been
# claiming to publish coverage while publishing none. Run the step only
# when the secret exists, and let a real upload failure fail the job.
# This is the shape the csharp and python pipeline templates already use:
# gate on the secret, pass it explicitly, and fail loudly when an upload
# that should work does not.
- name: Upload coverage
if: env.CODECOV_TOKEN != ''
uses: codecov/codecov-action@v7
with:
token: ${{ env.CODECOV_TOKEN }}
files: go/coverage.out
# `files` narrows what is uploaded but does not stop the CLI
# searching the workspace: it also found and uploaded
# experiments/test_coverage_data.json under the `go` flag.
disable_search: true
flags: go
fail_ci_if_error: true
- name: Note that coverage was not uploaded
if: env.CODECOV_TOKEN == ''
run: echo "::notice::CODECOV_TOKEN is not configured, so coverage was not uploaded"
publishRelease:
runs-on: ubuntu-latest
timeout-minutes: 15
needs: [findChangedGoFiles, test]
if: ${{ needs.findChangedGoFiles.outputs.isGoFilesChanged == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
concurrency:
group: ${{ github.workflow }}-publish-go
cancel-in-progress: false
permissions:
contents: write
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
submodules: true
- name: Read the module version
id: version-check
run: |
set -euo pipefail
PACKAGE_VERSION=$(tr -d '[:space:]' < VERSION)
if [ -z "$PACKAGE_VERSION" ]; then
echo "::error::go/VERSION is empty"
exit 1
fi
MODULE=$(sed -n 's/^module //p' go.mod | head -1)
echo "version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT"
echo "module=$MODULE" >> "$GITHUB_OUTPUT"
echo "Module: $MODULE@v$PACKAGE_VERSION"
# Go has no registry to push to: the module proxy serves whatever the
# tag points at, so creating the tag *is* the publish. That is why this
# job both releases and verifies, unlike the other languages.
- name: Create Go module tag and GitHub release
id: publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PACKAGE_VERSION: ${{ steps.version-check.outputs.version }}
run: |
set -euo pipefail
# Go modules living in the go/ subdirectory require go/vX.Y.Z tags.
TAG="go/v${PACKAGE_VERSION}"
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists"
echo "published=skipped" >> "$GITHUB_OUTPUT"
exit 0
fi
gh release create "$TAG" \
--title "[Go] $PACKAGE_VERSION" \
--notes "Go module release. Install with: \`go get github.com/link-foundation/links-notation/go@v$PACKAGE_VERSION\`"
echo "published=true" >> "$GITHUB_OUTPUT"
- name: Verify the module is really on proxy.golang.org
if: steps.publish.outputs.published == 'true'
env:
MODULE: ${{ steps.version-check.outputs.module }}
PACKAGE_VERSION: ${{ steps.version-check.outputs.version }}
run: |
set -euo pipefail
# The proxy only fetches a version once someone asks for it, so this
# also warms it for the first consumer.
for attempt in $(seq 1 20); do
if curl -fsS "https://proxy.golang.org/${MODULE}/@v/v${PACKAGE_VERSION}.info" >/dev/null 2>&1; then
echo "Verified ${MODULE}@v${PACKAGE_VERSION} on proxy.golang.org (attempt ${attempt})"
exit 0
fi
echo "Not on the proxy yet, retrying in 15s (attempt ${attempt}/20)"
sleep 15
done
echo "::error::${MODULE}@v${PACKAGE_VERSION} did not appear on proxy.golang.org within 5 minutes"
exit 1