aboutsummaryrefslogtreecommitdiffhomepage
path: root/.github/workflows/release.yml
blob: d788ca361ff52330927229843d96cb5e07131069 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
name: Release

on:
  push:
    tags:
      - 'v*.*.*'

jobs:
  release:
    name: Release
    strategy:
      matrix:
        os: 
          - ubuntu-latest
        go: 
          - '1.23'

        include:
        # Set the minimum Go patch version for the given Go minor
        # Usable via ${{ matrix.GO_SEMVER }}
        - go: '1.23'
          GO_SEMVER: '~1.23.0'

    runs-on: ${{ matrix.os }}
    # https://github.com/sigstore/cosign/issues/1258#issuecomment-1002251233
    # https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings
    permissions:
      id-token: write
      # https://docs.github.com/en/rest/overview/permissions-required-for-github-apps#permission-on-contents
      # "Releases" is part of `contents`, so it needs the `write`
      contents: write

    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Install Go
      uses: actions/setup-go@v5
      with:
        go-version: ${{ matrix.GO_SEMVER }}
        check-latest: true

    # Force fetch upstream tags -- because 65 minutes
    # tl;dr: actions/checkout@v4 runs this line:
    #   git -c protocol.version=2 fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +ebc278ec98bb24f2852b61fde2a9bf2e3d83818b:refs/tags/
    # which makes its own local lightweight tag, losing all the annotations in the process. Our earlier script ran:
    #   git fetch --prune --unshallow
    # which doesn't overwrite that tag because that would be destructive.
    # Credit to @francislavoie for the investigation.
    # https://github.com/actions/checkout/issues/290#issuecomment-680260080
    - name: Force fetch upstream tags
      run: git fetch --tags --force

    # https://github.community/t5/GitHub-Actions/How-to-get-just-the-tag-name/m-p/32167/highlight/true#M1027
    - name: Print Go version and environment
      id: vars
      run: |
        printf "Using go at: $(which go)\n"
        printf "Go version: $(go version)\n"
        printf "\n\nGo environment:\n\n"
        go env
        printf "\n\nSystem environment:\n\n"
        env
        echo "version_tag=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
        echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT

        # Add "pip install" CLI tools to PATH
        echo ~/.local/bin >> $GITHUB_PATH

        # Parse semver
        TAG=${GITHUB_REF/refs\/tags\//}
        SEMVER_RE='[^0-9]*\([0-9]*\)[.]\([0-9]*\)[.]\([0-9]*\)\([0-9A-Za-z\.-]*\)'
        TAG_MAJOR=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\1#"`
        TAG_MINOR=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\2#"`
        TAG_PATCH=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\3#"`
        TAG_SPECIAL=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\4#"`
        echo "tag_major=${TAG_MAJOR}" >> $GITHUB_OUTPUT
        echo "tag_minor=${TAG_MINOR}" >> $GITHUB_OUTPUT
        echo "tag_patch=${TAG_PATCH}" >> $GITHUB_OUTPUT
        echo "tag_special=${TAG_SPECIAL}" >> $GITHUB_OUTPUT

    # Cloudsmith CLI tooling for pushing releases
    # See https://help.cloudsmith.io/docs/cli
    - name: Install Cloudsmith CLI
      run: pip install --upgrade cloudsmith-cli

    - name: Validate commits and tag signatures
      run: |
        
        # Import Matt Holt's key
        curl 'https://github.com/mholt.gpg' | gpg --import

        echo "Verifying the tag: ${{ steps.vars.outputs.version_tag }}"
        # tags are only accepted if signed by Matt's key
        git verify-tag "${{ steps.vars.outputs.version_tag }}" || exit 1

    - name: Install Cosign
      uses: sigstore/cosign-installer@main
    - name: Cosign version
      run: cosign version
    - name: Install Syft
      uses: anchore/sbom-action/download-syft@main
    - name: Syft version
      run: syft version
    - name: Install xcaddy
      run: |
        go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
        xcaddy version
    # GoReleaser will take care of publishing those artifacts into the release
    - name: Run GoReleaser
      uses: goreleaser/goreleaser-action@v6
      with:
        version: latest
        args: release --clean --timeout 60m
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        TAG: ${{ steps.vars.outputs.version_tag }}
        COSIGN_EXPERIMENTAL: 1

    # Only publish on non-special tags (e.g. non-beta)
    # We will continue to push to Gemfury for the foreseeable future, although
    # Cloudsmith is probably better, to not break things for existing users of Gemfury.
    # See https://gemfury.com/caddy/deb:caddy
    - name: Publish .deb to Gemfury
      if: ${{ steps.vars.outputs.tag_special == '' }}
      env:
        GEMFURY_PUSH_TOKEN: ${{ secrets.GEMFURY_PUSH_TOKEN }}
      run: |
        for filename in dist/*.deb; do
          # armv6 and armv7 are both "armhf" so we can skip the duplicate
          if [[ "$filename" == *"armv6"* ]]; then
            echo "Skipping $filename"
            continue
          fi

          curl -F package=@"$filename" https://${GEMFURY_PUSH_TOKEN}:@push.fury.io/caddy/
        done

    # Publish only special tags (unstable/beta/rc) to the "testing" repo
    # See https://cloudsmith.io/~caddy/repos/testing/
    - name: Publish .deb to Cloudsmith (special tags)
      if: ${{ steps.vars.outputs.tag_special != '' }}
      env:
        CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
      run: |
        for filename in dist/*.deb; do
          # armv6 and armv7 are both "armhf" so we can skip the duplicate
          if [[ "$filename" == *"armv6"* ]]; then
            echo "Skipping $filename"
            continue
          fi

          echo "Pushing $filename to 'testing'"
          cloudsmith push deb caddy/testing/any-distro/any-version $filename
        done

    # Publish stable tags to Cloudsmith to both repos, "stable" and "testing"
    # See https://cloudsmith.io/~caddy/repos/stable/
    - name: Publish .deb to Cloudsmith (stable tags)
      if: ${{ steps.vars.outputs.tag_special == '' }}
      env:
        CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
      run: |
        for filename in dist/*.deb; do
          # armv6 and armv7 are both "armhf" so we can skip the duplicate
          if [[ "$filename" == *"armv6"* ]]; then
            echo "Skipping $filename"
            continue
          fi

          echo "Pushing $filename to 'stable'"
          cloudsmith push deb caddy/stable/any-distro/any-version $filename

          echo "Pushing $filename to 'testing'"
          cloudsmith push deb caddy/testing/any-distro/any-version $filename
        done