diff --git a/.dockerignore b/.dockerignore index 8bdf28c9..154be7f1 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,2 @@ -.build/ .swiftpm/ .git/ \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..62c44b3f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @Sherlouk @adamrushy diff --git a/.github/install-docker.bash b/.github/install-docker.bash new file mode 100755 index 00000000..84a33d71 --- /dev/null +++ b/.github/install-docker.bash @@ -0,0 +1,23 @@ +#!/bin/bash + +set -eu + +# Docker installation steps, from their own website: + +# Add Docker's official GPG key: +apt-get update -y +apt-get install ca-certificates curl gnupg -y +install -m 0755 -d /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg +chmod a+r /etc/apt/keyrings/docker.gpg + +# Add the repository to Apt sources: +# shellcheck source=/dev/null +echo \ + "deb [arch=\"$(dpkg --print-architecture)\" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ + \"$(. /etc/os-release && echo "$VERSION_CODENAME")\" stable" | + tee /etc/apt/sources.list.d/docker.list >/dev/null +apt-get update -y + +# Install Docker: +apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c26836cf..ccf3af3c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,7 @@ on: pull_request: push: branches: [main] + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -13,11 +14,47 @@ jobs: build: if: github.repository_owner == 'SwiftLeeds' runs-on: ubuntu-latest + container: swift:6.0-jammy steps: - name: Checkout Repository uses: actions/checkout@v4 + # actions/cache will detect zstd and will become much faster. + - name: Install zstd + run: | + apt-get update -y + apt-get install -y zstd + + - name: Restore .build + if: ${{ github.run_attempt == 1 }} # Because maybe the cache is causing issues + id: "restore-cache" + uses: actions/cache/restore@v4 + with: + path: .build + key: "build-${{ runner.os }}-${{ github.event.pull_request.base.sha || github.event.after }}" + restore-keys: "build-${{ runner.os }}-" + + - name: Prebuild Application + run: | + apt-get -q update \ + && apt-get -q dist-upgrade -y \ + && apt-get install -y libjemalloc-dev \ + && apt-get install -y libssl-dev \ + && rm -rf /var/lib/apt/lists/* + + swift build -c release --force-resolved-versions --static-swift-stdlib -Xlinker -ljemalloc + + - name: Cache .build + if: steps.restore-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: .build + key: "build-${{ runner.os }}-${{ github.event.pull_request.base.sha || github.event.after }}" + + - name: Install Docker + run: ./.github/install-docker.bash + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -35,8 +72,6 @@ jobs: push: true provenance: false tags: europe-west1-docker.pkg.dev/swiftleeds-website/swiftleeds-web/web:${{ github.head_ref || github.ref_name }}-latest - cache-from: type=gha - cache-to: type=gha,mode=max deploy: if: github.repository_owner == 'SwiftLeeds' diff --git a/.github/workflows/close.yml b/.github/workflows/close.yml index edd7b5e9..c7e2f6f8 100644 --- a/.github/workflows/close.yml +++ b/.github/workflows/close.yml @@ -19,7 +19,7 @@ jobs: uses: "google-github-actions/setup-gcloud@v2" - name: Destroy Google Cloud Run Servce - run: gcloud run services delete --quiet --region europe-west2 swiftleeds-web-${{ github.head_ref || github.ref_name }} + run: gcloud run services delete --quiet --region europe-west2 swiftleeds-web-${{ github.head_ref || github.ref_name }}-be - name: Destroy GitHub Deployment uses: bobheadxi/deployments@v1 diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index b0029532..e7d45d5d 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -2,6 +2,8 @@ name: Sync to Staging on: workflow_dispatch: + schedule: + - cron: '0 0 */5 * *' jobs: sync: diff --git a/Dockerfile b/Dockerfile index ea70f1a3..2c8393ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # ================================ # Build image # ================================ -FROM swift:5.10-noble as build +FROM swift:6.0-jammy AS build # Install OS updates RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ @@ -14,44 +14,36 @@ RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ # Set up a build area WORKDIR /build -# First just resolve dependencies. -# This creates a cached layer that can be reused -# as long as your Package.swift/Package.resolved -# files do not change. -COPY ./Package.* ./ -RUN swift package resolve \ - $([ -f ./Package.resolved ] && echo "--force-resolved-versions" || true) - # Copy entire repo into container COPY . . -# Build everything, with optimizations, with static linking, and using jemalloc -# N.B.: The static version of jemalloc is incompatible with the static Swift runtime. -RUN swift build -c release \ - --static-swift-stdlib \ - -Xlinker -ljemalloc - # Switch to the staging area WORKDIR /staging +# Copy pre-built app to staging area +COPY .build ./.build + # Copy main executable to staging area -RUN cp "$(swift build --package-path /build -c release --show-bin-path)/App" ./ +RUN mv .build/release/App ./ # Copy static swift backtracer binary to staging area RUN cp "/usr/libexec/swift/linux/swift-backtrace-static" ./ # Copy resources bundled by SPM to staging area -RUN find -L "$(swift build --package-path /build -c release --show-bin-path)/" -regex '.*\.resources$' -exec cp -Ra {} ./ \; +RUN find -L ".build/release/" -regex '.*\.resources$' -exec cp -Ra {} ./ \; # Copy any resources from the public directory and views directory if the directories exist # Ensure that by default, neither the directory nor any of its contents are writable. RUN [ -d /build/Public ] && { mv /build/Public ./Public && chmod -R a-w ./Public; } || true RUN [ -d /build/Resources ] && { mv /build/Resources ./Resources && chmod -R a-w ./Resources; } || true +# Remove .build directory +RUN rm -dr .build + # ================================ # Run image # ================================ -FROM ubuntu:noble +FROM ubuntu:jammy # Make sure all system packages are up to date, and install only essential packages. RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ diff --git a/Sources/App/entrypoint.swift b/Sources/App/entrypoint.swift index c753bd14..6cefd4d7 100644 --- a/Sources/App/entrypoint.swift +++ b/Sources/App/entrypoint.swift @@ -12,18 +12,20 @@ enum Entrypoint { let app = try await Application.make(env) // This attempts to install NIO as the Swift Concurrency global executor. - // You should not call any async functions before this point. + // You can enable it if you'd like to reduce the amount of context switching between NIO and Swift Concurrency. + // Note: this has caused issues with some libraries that use `.wait()` and cleanly shutting down. + // If enabled, you should be careful about calling async functions before this point as it can cause assertion failures. let executorTakeoverSuccess = NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor() - app.logger.info("Running with \(executorTakeoverSuccess ? "SwiftNIO" : "standard") Swift Concurrency default executor") + app.logger.info("Tried to install SwiftNIO's EventLoopGroup as Swift's global concurrency executor", metadata: ["success": .stringConvertible(executorTakeoverSuccess)]) do { try await configure(app) + try await app.execute() } catch { app.logger.report(error: error) try? await app.asyncShutdown() throw error } - try await app.execute() try await app.asyncShutdown() } }