synapsecns/sanguine

View on GitHub
.github/workflows/goreleaser-actions.yml

Summary

Maintainability
Test Coverage
name: Go Releaser
on: [push]

jobs:
  # build the goreleaser container cgo cross compiler container. Note: this wil not be applied  until
  # the next run since build-goreleaser and run-goreleaser are run concurrently. We expect github actions
  # to fix this in a future version.
  build-goreleaser:
    runs-on: ubuntu-latest
    outputs:
      goreleaser-image: ${{ steps.name-export.outputs.TAG_NAME }}
    permissions:
      # always required
      packages: write
      # only required for private repos
      actions: read
      contents: write
    steps:
      - name: Git Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # needed if using new-from-rev (see: https://golangci-lint.run/usage/configuration/#issues-configuration)


      - name: Cache Docker images.
        uses: ScribeMD/docker-cache@0.3.6
        with:
          key: docker-release-${{ runner.os }}-${{ matrix.package }}

      - uses: dorny/paths-filter@v3
        name: check if any changes warrant a new build of goreleaser-cgo-cross-compiler
        id: changes
        with:
          token:  ${{ secrets.GITHUB_TOKEN }}
          filters: |
            src:
              - 'docker/goreleaser/**'
      -
        name: Set up Docker Buildx
        if: steps.changes.outputs.src == 'true'
        uses: docker/setup-buildx-action@v2
        with:
          driver-opts: network=host

      - name: Environment variables
        # TODO: this if block needs to be run on every step now, but should be fixed in a future version: https://github.com/actions/runner/issues/662
        if: steps.changes.outputs.src == 'true'
        uses: franzdiebold/github-env-vars-action@v1.0.0
        env:
          ACTIONS_ALLOW_UNSECURE_COMMANDS: true

      -
        name: Login to GitHub Container Registry
        if: steps.changes.outputs.src == 'true'
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # we do this so we can use it in subseuqnet steps
      - name: Export latest tag name
        id: name-export
        run:
          echo "##[set-output name=TAG_NAME;]$(echo $LATEST_TAG_NAME)"
        env:
          LATEST_TAG_NAME: ghcr.io/synapsecns/sanguine-goreleaser:${{ hashFiles('docker/goreleaser/**') }}
      -
        name: Build and push
        if: steps.changes.outputs.src == 'true'
        uses: docker/build-push-action@v3
        with:
          context: .
          push: true
          file: ./docker/goreleaser/Dockerfile
          # TODO this needs to be versioned
          # Note: this automatically pushes the latest tag for sanguine-goreleaser even on branched workflows. While unlikely,
          # this could break local devnets that rely on working versions of this image and as such the latest tag should only be pushed on master
          # additionally, tags representing a specific version rather than the hash of the file should be considered for future use.
          tags: ghcr.io/synapsecns/sanguine-goreleaser:latest,${{ steps.name-export.outputs.TAG_NAME }}
          cache-from: type=registry,ref=ghcr.io/synapsecns/sanguine-goreleaser:buildcache
          cache-to: type=registry,ref=ghcr.io/synapsecns/sanguine-goreleaser:buildcache,mode=max

  # TODO: we should find a way for this not to be duplicated with go.yml
  changes:
    name: Change Detection
    runs-on: ubuntu-latest
    # see: https://stackoverflow.com/a/68414395
    if: ${{ format('refs/heads/{0}', github.event.repository.default_branch) == github.ref || contains(github.event.head_commit.message, '[goreleaser]') }}
    outputs:
      # Expose matched filters as job 'packages' output variable
      packages: ${{ steps.filter_go.outputs.changed_modules_deps }}
      package_count: ${{ steps.length.outputs.FILTER_LENGTH }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: docker://ghcr.io/synapsecns/sanguine/git-changes-action:latest
        id: filter_go
        with:
          github_token: ${{ secrets.WORKFLOW_PAT || secrets.GITHUB_TOKEN }}

      - id: length
        run: |
          export FILTER_LENGTH=$(echo $FILTERED_PATHS | jq '. | length')
          echo "##[set-output name=FILTER_LENGTH;]$(echo $FILTER_LENGTH)"
        env:
          FILTERED_PATHS: ${{ steps.filter_go.outputs.changed_modules_deps }}

  run-goreleaser:
    runs-on:
      - namespace-profile-fast-goreleaser
    needs: [build-goreleaser,changes]
    if: ${{ needs.changes.outputs.package_count > 0 }}
    permissions:
      id-token: write
      # always required
      packages: write
      # only required for private repos
      actions: read
      contents: write
    strategy:
      fail-fast: false
      matrix:
        # list of packages, if you update this update changes as well
        package: ${{ fromJSON(needs.changes.outputs.packages) }}
    container:
      image: ${{ needs.build-goreleaser.outputs.goreleaser-image }}
      env:
        NSC_CACHE_PATH: ${{ env.NSC_CACHE_PATH }} # env.NSC_CACHE_PATH contains the path to Cache Volume directory, that is `/cache`.
      volumes:
        - /repo
        - /cache:/cache
      options: --cap-add=SYS_ADMIN # Required to by nscloud-cache-action to call `mount`.

    steps:
      - name: Git Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: ${{ github.ref == 'refs/heads/master' && '0' || '1' }}

      - name: Set up cache
        if:  ${{ !contains(runner.name, 'nsc') }}
        uses: actions/cache@v4
        with:
          path: |
            ~/.cache/go-build
            ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{matrix.package}}-${{ hashFiles(format('{0}/go.sum', matrix.package)) }}
          restore-keys: |
            ${{ runner.os }}-go-${{matrix.package}}

      - name: Setup Go cache
        uses: namespacelabs/nscloud-cache-action@v1
        if:  ${{ contains(runner.name, 'nsc') }}
        with:
          cache: go

      - name: Get branch name
        id: branch-name
        uses: tj-actions/branch-names@v6

      - name: Bump version and push tag
        id: tag_version
        if: steps.branch-name.outputs.is_default == 'true'
        uses: mathieudutour/github-tag-action@v6.0
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          tag_prefix: ${{matrix.package}}/v
          release_branches: master
          fetch_all_tags: true

      - name: Tag Config
        run: git config --global --add safe.directory /__w/sanguine/sanguine

      -
        name: Fetch all tags
        if: steps.branch-name.outputs.is_default == 'true'
        run: git fetch --force --tags

      # get the tag we just created
      - name: Git Fetch Unshallow
        if: steps.branch-name.outputs.is_default == 'true'
        run: git fetch

      - name: Import GPG key
        uses: crazy-max/ghaction-import-gpg@111c56156bcc6918c056dbef52164cfa583dc549 # v5.2.0
        id: import_gpg
        with:
          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
          passphrase: ${{ secrets.GPG_PASSPHRASE }}

      -
        name: Login to GitHub Container Registry
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Run GoReleaser (Release)
        if: steps.branch-name.outputs.is_default == 'true'
        run: goreleaser  --timeout 900m --clean --debug -f ${{matrix.package}}/.goreleaser.yml
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GONOSUM: '.*'
          GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
          GOGC: 2000
          GOMEMLIMIT: 6GiB
          GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}

      # use this to determine if we need goreleaser for a workflow
      - name: Check For Docker Images
        if: steps.branch-name.outputs.is_default != 'true'
        id: image_check
        run: |
          # will be 0 if none present
          has_images=$(yq eval '.dockers != null' ${{matrix.package}}/.goreleaser.yml)
          echo "##[set-output name=has_images;]$(echo $has_images)"

      - name: Run GoReleaser (Snapshot)
        if: steps.branch-name.outputs.is_default != 'true' && steps.image_check.outputs.has_images == 'true'
        run: goreleaser --timeout 900m --snapshot --clean --debug -f ${{matrix.package}}/.goreleaser.yml
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GONOSUM: '.*'
          GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
          GOGC: 20
          GOMEMLIMIT: 6GiB
          GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}

      - name: Get Project Name
        id: project_id
        run: |
          project_name=$(yq '.project_name' ${{matrix.package}}/.goreleaser.yml)
          echo "##[set-output name=project_name;]$(echo $project_name)"

      - name: Push Docker Images (Snapshot)
        if: steps.branch-name.outputs.is_default != 'true' && steps.image_check.outputs.has_images == 'true'
        run: |
          # Load the number of docker configurations
          docker_configs=$(yq e '.dockers | length' dist/config.yaml)

          # Check if there are no docker configurations
          if [ "$docker_configs" -eq "0" ]; then
            echo "No docker images to push"
            exit 0
          fi

          # Iterate through each docker configuration
            i=0
          while [ "$i" -lt "$docker_configs" ]; do
            # Extract the first image template
            image_template=$(yq e ".dockers[$i].image_templates[0]" dist/config.yaml)

            # Extract the base name from the image template
            image_name=$(echo "$image_template" | sed -E 's|^(.*):[^:]+$|\1|')

            # Tag and push the docker image
            docker tag "$image_name:latest" "$image_name:${GITHUB_SHA}"
            docker push "$image_name:${GITHUB_SHA}"

            i=$((i + 1))
          done
        env:
          image_name: ${{ steps.project_id.outputs.project_name }}

      - name: Zip Artifacts (Snapshot)
        if: steps.branch-name.outputs.is_default != 'true' && steps.image_check.outputs.has_images == 'true'
        run: |
            ls
            zip -rv ${{ steps.project_id.outputs.project_name }}.zip dist

      - name: Push Artifacts (Snapshot)
        if: steps.branch-name.outputs.is_default != 'true' && steps.image_check.outputs.has_images == 'true'
        uses: actions/upload-artifact@v4
        with:
          name: ${{steps.project_id.outputs.project_name}}.zip
          path: ${{steps.project_id.outputs.project_name}}.zip

      - name: Refresh Report Card
        if: steps.branch-name.outputs.is_default == 'true'
        working-directory: ${{matrix.package}}/
        run: |
          module_name=$(go mod edit -json | jq -r '.Module.Path')
          curl -X POST -F "repo=$module_name" https://goreportcard.com/checks