.github/workflows/integration-tests.yml
.github/workflows/integration-tests.ymlBrowse 1970 files
3,772 tokens
14,221 bytes
Token encoding: o200k_base
Snapshot 21a254f
← Back to SKILL.md
1name: Integration Tests2 3on:4 push:5 branches: [main]6 paths-ignore:7 - "docs/**"8 pull_request:9 paths-ignore:10 - "docs/**"11 schedule:12 # run every day to alert against breaking changes in mise backends13 # the backends used for various projects (in registry.toml) are compiled into the mise binary, but the code that runs14 # these backends, and the services they connect to are not. By running integration tests each day, we'll know if15 # something changes in the package ecosystem that breaks one of our build tests.16 - cron: "0 0 * * *"17 18env:19 MISE_ENV: ci20 21jobs:22 # generate a JSON array of all entries in examples/ to shard out example into a separate job that can be run in parallel23 find-examples:24 runs-on: ubuntu-latest25 outputs:26 examples: ${{ steps.find-examples.outputs.examples }}27 steps:28 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd29 - name: Find examples with test.json30 id: find-examples31 run: |32 examples=$(find examples -name "test.json" -exec dirname {} \; | xargs -n 1 basename | jq -R -s -c 'split("\n")[:-1]')33 echo "examples=$examples" >> "$GITHUB_OUTPUT"34 35 # integration tests plan, build, and run example projects. Running the example projects requires both the builder and runtime36 # images. If the mise version in version.txt is newer than origin/main, we build fresh images containing the pinned mise version.37 # The code below builds these required images, and pushes them to a production registry so they can be used properly in the38 # matrix jobs (which all run in an isolated runner, so they must reference a remote registry).39 # These images are *not* tagged with `latest` or other more generic tags used in the release workflow. Additionally,40 # since railpack uses a image tag from the mise version compiled into the binary, pushes to the registry should not cause41 # any production impacts.42 build-images:43 runs-on: ubuntu-latest44 permissions:45 packages: write46 contents: read47 steps:48 # duplicated on the release.yml job, keep these in sync49 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd50 with:51 # we need the full history to compare the mise version against origin/main52 fetch-depth: 053 - uses: jdx/mise-action@1648a7812b9aeae629881980618f07993286915154 - uses: docker/setup-docker-action@77e84dbf09b47d1e29270283c22f16145aa85ca155 with:56 version: ${{ env.DOCKER_VERSION }}57 - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c358 - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df559 - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee60 with:61 registry: ${{ env.PRODUCTION_CONTAINER_REGISTRY }}62 username: ${{ github.actor }}63 password: ${{ secrets.GITHUB_TOKEN }}64 65 # since railpack binary is pinned to a specific image version, we only want to build and push the images if the mise version66 # in version.txt is newer than what is on origin/main. We intentionally only trigger on upgrades to avoid mutating images67 # that may already be referenced by released railpack binaries or production usage.68 # Mise versions are updated frequently, which means we'll pick up any updates to the `FROM` images regularly, so we shouldn't need69 # to update these images outside of a mise version update.70 - name: Check for image input changes71 id: mise_version72 run: |73 if mise run mise-version-newer-than-main; then74 echo "changed=true" >> "$GITHUB_OUTPUT"75 else76 echo "changed=false" >> "$GITHUB_OUTPUT"77 fi78 79 80 - name: Build Builder Image81 uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf82 if: steps.mise_version.outputs.changed == 'true'83 with:84 context: images/debian/build85 platforms: linux/amd64,linux/arm6486 tags: ${{ env.PRODUCTION_BUILDER_IMAGE_REF }}87 build-args: MISE_VERSION=${{ env.RAILPACK_MISE_VERSION }}88 push: true89 # instead of figuring out the right "what's changed" mechanism90 cache-from: type=registry,ref=${{ env.PRODUCTION_BUILDER_IMAGE_REF }}91 # embed cache metadata into the image being pushed so it can be used on the next run against this PR92 cache-to: type=inline93 94 - name: Build Runtime Image95 uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf96 if: steps.mise_version.outputs.changed == 'true'97 with:98 context: images/debian/runtime99 platforms: linux/amd64,linux/arm64100 tags: ${{ env.PRODUCTION_RUNTIME_IMAGE_REF }}101 push: true102 cache-from: type=registry,ref=${{ env.PRODUCTION_BUILDER_IMAGE_REF }}103 cache-to: type=inline104 105 frontend-e2e:106 name: Frontend E2E107 needs: [build-images, find-examples]108 runs-on: ubuntu-latest109 steps:110 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd111 - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151112 113 # GitHub's preinstalled Docker 28 uses the classic image store, which disables114 # Railpack's merge operations. Fresh Docker 29+ daemons use containerd by default.115 - uses: docker/setup-docker-action@77e84dbf09b47d1e29270283c22f16145aa85ca1116 with:117 version: ${{ env.DOCKER_VERSION }}118 119 - name: Build Frontend Image120 run: |121 docker build \122 -f images/alpine/frontend/Dockerfile \123 -t railpack-frontend:local \124 .125 126 - name: Select Random Example127 id: example128 env:129 EXAMPLES: ${{ needs.find-examples.outputs.examples }}130 run: |131 example=$(jq -r '.[]' <<< "$EXAMPLES" | shuf -n 1)132 echo "Selected $example"133 echo "name=$example" >> "$GITHUB_OUTPUT"134 135 - name: Generate Build Plan136 env:137 EXAMPLE: ${{ steps.example.outputs.name }}138 run: |139 mkdir -p tmp/frontend-plan140 mise run cli plan "examples/$EXAMPLE" --out tmp/frontend-plan/railpack-plan.json141 142 - name: Build Example With Frontend Image143 env:144 EXAMPLE: ${{ steps.example.outputs.name }}145 run: |146 # This smoke test does not load test.json, so provide non-sensitive values147 # for every plan-declared secret through BuildKit's secret mount interface.148 secret_args=()149 while IFS= read -r secret; do150 export "$secret=frontend-e2e"151 secret_args+=(--secret "id=$secret,env=$secret")152 done < <(jq -r '.secrets[]?' tmp/frontend-plan/railpack-plan.json)153 154 docker buildx build \155 --build-arg BUILDKIT_SYNTAX="railpack-frontend:local" \156 "${secret_args[@]}" \157 -f tmp/frontend-plan/railpack-plan.json \158 "examples/$EXAMPLE"159 160 test:161 needs: [build-images, find-examples]162 runs-on: ubuntu-latest163 strategy:164 matrix:165 # create a separate job for each entry in examples/. i.e. gleam, elixir-ecto, etc166 example: ${{ fromJson(needs.find-examples.outputs.examples) }}167 fail-fast: false168 169 steps:170 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd171 - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151172 - uses: docker/setup-docker-action@77e84dbf09b47d1e29270283c22f16145aa85ca1173 with:174 version: ${{ env.DOCKER_VERSION }}175 176 - name: Cache Go modules and build artifacts177 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae178 with:179 # ~/.cache/go-build -> GOCACHE (Compiled build artifacts)180 # ~/go/pkg/mod -> GOMODCACHE (Downloaded dependencies)181 path: |182 ~/.cache/go-build183 ~/go/pkg/mod184 key: go-${{ runner.os }}-${{ hashFiles('mise.lock') }}-${{ hashFiles('go.sum') }}185 restore-keys: |186 go-${{ runner.os }}-${{ hashFiles('mise.lock') }}-187 188 # not strictly required, but authenticating increases rate limits and reduces 429 errors189 - name: Log in to GitHub Container Registry190 uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee191 with:192 registry: ${{ env.PRODUCTION_CONTAINER_REGISTRY }}193 username: ${{ github.actor }}194 password: ${{ secrets.GITHUB_TOKEN }}195 196 # intermittent docker pull failures kept failing the entire pipeline, which is why we are retrying197 - name: Start BuildKit198 run: mise run retry --attempts 5 -- mise run run-buildkit-container199 200 # required for integration tests that use multiple platforms201 - name: Set up QEMU202 uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3203 204 - name: Run test for ${{ matrix.example }}205 env:206 # without the GITHUB_TOKEN, mise will 403 us207 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}208 # each test should be run with an exact match otherwise `gleam` will also match `gleam-custom` causing duplicate test runs209 run: |210 go test -v ./integration_tests \211 -run "^TestExamplesIntegration/${{ matrix.example }}$" \212 -timeout 20m213 214 - name: Upload size.json215 if: always()216 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a217 with:218 name: size-x86-${{ matrix.example }}219 path: examples/${{ matrix.example }}/size.json220 if-no-files-found: ignore221 222 - name: Stop BuildKit223 if: always()224 run: docker stop buildkit225 226 # Same as `test`, but runs on native ARM hardware to catch ARM-specific failures227 # without QEMU emulation overhead.228 test-arm:229 needs: [build-images, find-examples]230 runs-on: ubuntu-24.04-arm231 strategy:232 matrix:233 example: ${{ fromJson(needs.find-examples.outputs.examples) }}234 fail-fast: false235 236 steps:237 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd238 - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151239 - uses: docker/setup-docker-action@77e84dbf09b47d1e29270283c22f16145aa85ca1240 with:241 version: ${{ env.DOCKER_VERSION }}242 243 - name: Cache Go modules and build artifacts244 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae245 with:246 path: |247 ~/.cache/go-build248 ~/go/pkg/mod249 # include runner.arch so ARM and x86 caches don't collide250 key: go-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('mise.lock') }}-${{ hashFiles('go.sum') }}251 restore-keys: |252 go-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('mise.lock') }}-253 254 - name: Log in to GitHub Container Registry255 uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee256 with:257 registry: ${{ env.PRODUCTION_CONTAINER_REGISTRY }}258 username: ${{ github.actor }}259 password: ${{ secrets.GITHUB_TOKEN }}260 261 - name: Start BuildKit262 run: mise run run-buildkit-container263 264 # QEMU provides x86 emulation on this ARM runner for any cross-platform build steps265 - name: Set up QEMU266 uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3267 268 - name: Run test for ${{ matrix.example }}269 env:270 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}271 run: |272 go test -v ./integration_tests \273 -run "^TestExamplesIntegration/${{ matrix.example }}$" \274 -timeout 20m275 276 - name: Upload size.json277 if: always()278 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a279 with:280 name: size-arm-${{ matrix.example }}281 path: examples/${{ matrix.example }}/size.json282 if-no-files-found: ignore283 284 - name: Stop BuildKit285 if: always()286 run: docker stop buildkit287 288 # Collects size.json artifacts from both x86 and ARM matrix jobs, merges them289 # into a single benchmark payload with arch-suffixed names (e.g. "node-bun (x86)").290 # On main, pushes results to gh-pages to track history.291 # On PRs, compares against stored history and posts a comment with the delta.292 docker-image-benchmark:293 needs: [test, test-arm]294 runs-on: ubuntu-latest295 permissions:296 # required for benchmark-action to push to gh-pages on main297 contents: write298 # required to post comparison comments on PRs299 pull-requests: write300 301 steps:302 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd303 304 - name: Download all size artifacts305 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c306 # each artifact lands in its own size-{x86,arm}-<example>/ directory307 with:308 pattern: size-*309 merge-multiple: false310 311 - name: Merge size.json files into benchmark format312 # customSmallerIsBetter expects [{name, unit, value}]; suffix name with arch for side-by-side comparison313 run: |314 # create a master size.json for manual debugging315 jq -s 'map(. + {name: (.name + " (x86)")})' size-x86-*/size.json > x86.json316 jq -s 'map(. + {name: (.name + " (arm)")})' size-arm-*/size.json > arm.json317 # this debugging data is compared against the "master" copy in gh-pages318 jq -s 'add | sort_by(.name) | map({name, unit: "bytes", value: .size})' x86.json arm.json > benchmark.json319 320 - name: Store benchmark results321 uses: benchmark-action/github-action-benchmark@52576c92bccf6ac60c8223ec7eb2565637cae9ba322 with:323 tool: customSmallerIsBetter324 output-file-path: benchmark.json325 github-token: ${{ secrets.GITHUB_TOKEN }}326 # only persist history on main327 auto-push: ${{ github.ref == 'refs/heads/main' }}328 benchmark-data-dir-path: dev/bench329 # alert when an image grows more than 20%330 alert-threshold: "120%"331 # Enable Job Summary for PRs332 summary-always: true333 # no comment-on-alert, comment-always; just on PR334