instruction: Create a GitHub Actions workflow for CI/CD automation with automated testing
output: ## ci.yml
  
  **Repository:** prometheus/prometheus
  **File Type:** Github Workflow
  **Purpose:** Production configuration from prometheus project
  
  ```yaml
  ---
  name: CI
  on:
    pull_request:
    push:
  
  permissions:
    contents: read
  
  jobs:
    test_go:
      name: Go tests
      runs-on: ubuntu-latest
      container:
        # Whenever the Go version is updated here, .promu.yml
        # should also be updated.
        image: quay.io/prometheus/golang-builder:1.24-base
      steps:
        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
        - uses: ./.github/promci/actions/setup_environment
          with:
            enable_npm: true
        - run: make GO_ONLY=1 SKIP_GOLANGCI_LINT=1
        - run: go test ./tsdb/ -test.tsdb-isolation=false
        - run: make -C documentation/examples/remote_storage
        - run: make -C documentation/examples
  
    test_go_more:
      name: More Go tests
      runs-on: ubuntu-latest
      container:
        image: quay.io/prometheus/golang-builder:1.24-base
      steps:
        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
        - uses: ./.github/promci/actions/setup_environment
        - run: go test --tags=dedupelabels ./...
        - run: go test --tags=slicelabels -race ./cmd/prometheus ./prompb/io/prometheus/client
        - run: go test --tags=forcedirectio -race ./tsdb/
        - run: GOARCH=386 go test ./...
        - uses: ./.github/promci/actions/check_proto
          with:
            version: "3.15.8"
  
    test_go_oldest:
      name: Go tests with previous Go version
      runs-on: ubuntu-latest
      env:
        # Enforce the Go version.
        GOTOOLCHAIN: local
      container:
        # The go version in this image should be N-1 wrt test_go.
        image: quay.io/prometheus/golang-builder:1.23-base
      steps:
        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - run: make build
        # Don't run NPM build; don't run race-detector.
        - run: make test GO_ONLY=1 test-flags=""
  
    test_ui:
      name: UI tests
      runs-on: ubuntu-latest
      # Whenever the Go version is updated here, .promu.yml
      # should also be updated.
      container:
        image: quay.io/prometheus/golang-builder:1.24-base
  
      steps:
        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
        - uses: ./.github/promci/actions/setup_environment
          with:
            enable_go: false
            enable_npm: true
        - run: make assets-tarball
        - run: make ui-lint
        - run: make ui-test
        - uses: ./.github/promci/actions/save_artifacts
          with:
            directory: .tarballs
  
    test_windows:
      name: Go tests on Windows
      runs-on: windows-latest
      steps:
        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
          with:
            go-version: 1.24.x
        - run: |
            $TestTargets = go list ./... | Where-Object { $_ -NotMatch "(github.com/prometheus/prometheus/config|github.com/prometheus/prometheus/web)"}
            go test $TestTargets -vet=off -v
          shell: powershell
  
    test_mixins:
      name: Mixins tests
      runs-on: ubuntu-latest
      # Whenever the Go version is updated here, .promu.yml
      # should also be updated.
      container:
        image: quay.io/prometheus/golang-builder:1.24-base
      steps:
        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - run: go install ./cmd/promtool/.
        - run: go install github.com/google/go-jsonnet/cmd/jsonnet@latest
        - run: go install github.com/google/go-jsonnet/cmd/jsonnetfmt@latest
        - run: go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest
        - run: make -C documentation/prometheus-mixin clean
        - run: make -C documentation/prometheus-mixin jb_install
        - run: make -C documentation/prometheus-mixin
        - run: git diff --exit-code
  
    build:
      name: Build Prometheus for common architectures
      runs-on: ubuntu-latest
      if: |
        !(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v2.'))
        &&
        !(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v3.'))
        &&
        !(github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release-'))
        &&
        !(github.event_name == 'push' && github.event.ref == 'refs/heads/main')
      strategy:
        matrix:
          thread: [ 0, 1, 2 ]
      steps:
        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
        - uses: ./.github/promci/actions/build
          with:
            promu_opts: "-p linux/amd64 -p windows/amd64 -p linux/arm64 -p darwin/amd64 -p darwin/arm64 -p linux/386"
            parallelism: 3
            thread: ${{ matrix.thread }}
    build_all:
      name: Build Prometheus for all architectures
      runs-on: ubuntu-latest
      if: |
        (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v2.'))
        ||
        (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v3.'))
        ||
        (github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release-'))
        ||
        (github.event_name == 'push' && github.event.ref == 'refs/heads/main')
      strategy:
        matrix:
          thread: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]
  
      # Whenever the Go version is updated here, .promu.yml
      # should also be updated.
      steps:
        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
        - uses: ./.github/promci/actions/build
          with:
            parallelism: 12
            thread: ${{ matrix.thread }}
    build_all_status:
      # This status check aggregates the individual matrix jobs of the "Build
      # Prometheus for all architectures" step into a final status. Fails if a
      # single matrix job fails, succeeds if all matrix jobs succeed.
      # See https://github.com/orgs/community/discussions/4324 for why this is
      # needed
      name: Report status of build Prometheus for all architectures
      runs-on: ubuntu-latest
      needs: [build_all]
      # The run condition needs to include always(). Otherwise actions
      # behave unexpected:
      # only "needs" will make the Status Report be skipped if one of the builds fails https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-jobs-in-a-workflow#defining-prerequisite-jobs
      # And skipped is treated as success https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborat[…]n-repositories-with-code-quality-features/about-status-checks
      # Adding always ensures that the status check is run independently of the
      # results of Build All
      if: always() && github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release-')
      steps:
        - name: Successful build
          if: ${{ !(contains(needs.*.result, 'failure')) && !(contains(needs.*.result, 'cancelled')) }}
          run: exit 0
        - name: Failing or cancelled build
          if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
          run: exit 1
    check_generated_parser:
      name: Check generated parser
      runs-on: ubuntu-latest
      steps:
        - name: Checkout repository
          uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - name: Install Go
          uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
          with:
            cache: false
            go-version: 1.24.x
        - name: Run goyacc and check for diff
          run: make install-goyacc check-generated-parser
    golangci:
      name: golangci-lint
      runs-on: ubuntu-latest
      steps:
        - name: Checkout repository
          uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - name: Install Go
          uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
          with:
            go-version: 1.24.x
        - name: Install snmp_exporter/generator dependencies
          run: sudo apt-get update && sudo apt-get -y install libsnmp-dev
          if: github.repository == 'prometheus/snmp_exporter'
        - name: Lint with stringlabels
          uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
          with:
            args: --verbose --build-tags=stringlabels
            # Make sure to sync this with Makefile.common and scripts/golangci-lint.yml.
            version: v2.2.1
        - name: Lint with slicelabels
          uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
          with:
            args: --verbose --build-tags=slicelabels
            # Make sure to sync this with Makefile.common and scripts/golangci-lint.yml.
            version: v2.2.1
        - name: Lint with dedupelabels
          uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
          with:
            args: --verbose --build-tags=dedupelabels
            # Make sure to sync this with Makefile.common and scripts/golangci-lint.yml.
            version: v2.2.1
    fuzzing:
      uses: ./.github/workflows/fuzzing.yml
      if: github.event_name == 'pull_request'
    codeql:
      uses: ./.github/workflows/codeql-analysis.yml
      permissions:
        contents: read
        security-events: write
  
    publish_main:
      name: Publish main branch artifacts
      runs-on: ubuntu-latest
      needs: [test_ui, test_go, test_go_more, test_go_oldest, test_windows, golangci, codeql, build_all]
      if: github.event_name == 'push' && github.event.ref == 'refs/heads/main'
      steps:
        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
        - uses: ./.github/promci/actions/publish_main
          with:
            docker_hub_login: ${{ secrets.docker_hub_login }}
            docker_hub_password: ${{ secrets.docker_hub_password }}
            quay_io_login: ${{ secrets.quay_io_login }}
            quay_io_password: ${{ secrets.quay_io_password }}
    publish_release:
      name: Publish release artefacts
      runs-on: ubuntu-latest
      needs: [test_ui, test_go, test_go_more, test_go_oldest, test_windows, golangci, codeql, build_all]
      if: |
        (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v2.'))
        ||
        (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v3.'))
      steps:
        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
        - uses: ./.github/promci/actions/publish_release
          with:
            docker_hub_login: ${{ secrets.docker_hub_login }}
            docker_hub_password: ${{ secrets.docker_hub_password }}
            quay_io_login: ${{ secrets.quay_io_login }}
            quay_io_password: ${{ secrets.quay_io_password }}
            github_token: ${{ secrets.PROMBOT_GITHUB_TOKEN }}
    publish_ui_release:
      name: Publish UI on npm Registry
      runs-on: ubuntu-latest
      needs: [test_ui, codeql]
      steps:
        - name: Checkout
          uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
          with:
            persist-credentials: false
        - uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
        - name: Install nodejs
          uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
          with:
            node-version-file: "web/ui/.nvmrc"
            registry-url: "https://registry.npmjs.org"
        - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
          with:
            path: ~/.npm
            key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
            restore-keys: |
              ${{ runner.os }}-node-
        - name: Check libraries version
          if: |
            (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v2.'))
            ||
            (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v3.'))
          run: ./scripts/ui_release.sh --check-package "$(./scripts/get_module_version.sh ${GH_REF_NAME})"
          env:
            GH_REF_NAME: ${{ github.ref_name }}
        - name: build
          run: make assets
        - name: Copy files before publishing libs
          run: ./scripts/ui_release.sh --copy
        - name: Publish dry-run libraries
          if: |
            !(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v2.'))
            &&
            !(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v3.'))
          run: ./scripts/ui_release.sh --publish dry-run
        - name: Publish libraries
          if: |
            (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v2.'))
            ||
            (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v3.'))
          run: ./scripts/ui_release.sh --publish
          env:
            # The setup-node action writes an .npmrc file with this env variable
            # as the placeholder for the auth token
            NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
  
  ```
  
  This GitHub Actions workflow demonstrates production CI/CD practices including automated testing, building, and deployment.