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
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions: {}

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    permissions:
      contents: read # checkout
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
        with:
          persist-credentials: false
      - uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1
        with:
          bundler-cache: true
      - run: bundle exec rake test check

  zizmor:
    name: Audit workflows
    runs-on: ubuntu-latest
    permissions:
      contents: read # checkout
      security-events: write # upload SARIF to code scanning
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
        with:
          persist-credentials: false
      - uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2

  build:
    name: Build
    needs: test
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    permissions:
      contents: write # create GitHub release
      packages: write # push container image to GHCR
    outputs:
      version: ${{ steps.meta.outputs.version }}
      image: ghcr.io/${{ github.repository }}:${{ steps.meta.outputs.version }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
        with:
          persist-credentials: false
      - uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1
        with:
          bundler-cache: false
      - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
      - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - id: short-sha
        run: echo "sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
      - id: change-id
        run: echo "id=$(git cat-file -p HEAD | grep '^change-id ' | cut -d' ' -f2)" >> "$GITHUB_OUTPUT"
      - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
        id: meta
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=raw,value={{date 'YYYYMMDD'}}-${{ steps.short-sha.outputs.sha }}
      - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            COMMIT_SHA=${{ steps.short-sha.outputs.sha }}
            CHANGE_ID=${{ steps.change-id.outputs.id }}
            BUILD_DATE=${{ github.event.head_commit.timestamp }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
      - env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          STEPS_META_OUTPUTS_VERSION: ${{ steps.meta.outputs.version }}
        run: gh release create "${STEPS_META_OUTPUTS_VERSION}" --generate-notes

  snapshots:
    name: Snapshots
    needs: build
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: macos-latest
    permissions:
      contents: write # upload release assets
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
        with:
          persist-credentials: false
      - uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1
        with:
          bundler-cache: true
      - run: |
          output=$(bundle exec rake snapshots:capture)
          dir=$(echo "$output" | jq -r .output_dir)
          tar czf snapshots.tar.gz -C "$dir" .
      - env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NEEDS_BUILD_OUTPUTS_VERSION: ${{ needs.build.outputs.version }}
        run: gh release upload "${NEEDS_BUILD_OUTPUTS_VERSION}" snapshots.tar.gz

  deploy:
    name: Deploy to Fly.io
    needs: build
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    permissions:
      contents: read
    environment: demo
    concurrency:
      group: fly-deploy
      cancel-in-progress: true
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
        with:
          persist-credentials: false
      - uses: superfly/flyctl-actions/setup-flyctl@63da3ecc5e2793b98a3f2519b3d75d4f4c11cec2 # master
      - run: flyctl deploy --image ${NEEDS_BUILD_OUTPUTS_IMAGE}
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
          NEEDS_BUILD_OUTPUTS_IMAGE: ${{ needs.build.outputs.image }}

  pages:
    name: Deploy to Pages
    needs: [build, snapshots]
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    permissions:
      contents: read # download release assets
      id-token: write # OIDC token for Pages deployment
      pages: write # publish to GitHub Pages
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    concurrency:
      group: pages
      cancel-in-progress: true
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
        with:
          persist-credentials: false
      - uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1
        with:
          bundler-cache: true
      - env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NEEDS_BUILD_OUTPUTS_VERSION: ${{ needs.build.outputs.version }}
        run: |
          mkdir -p gallery
          gh release download "${NEEDS_BUILD_OUTPUTS_VERSION}" --pattern "snapshots.tar.gz" --output snapshots.tar.gz
          tar xzf snapshots.tar.gz -C gallery
          bundle exec rake "snapshots:gallery[gallery,gallery/index.html]"
          cp public/favicon.svg gallery/
      - uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
        with:
          path: gallery
      - id: deployment
        uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4