diff --git a/.github/workflows/publish-gcp-images.yml b/.github/workflows/publish-gcp-images.yml new file mode 100644 index 0000000000..01161adb75 --- /dev/null +++ b/.github/workflows/publish-gcp-images.yml @@ -0,0 +1,168 @@ +name: "☁️ Publish Images to GCP" + +on: + workflow_call: + inputs: + image_tag: + description: The image tag to publish + type: string + required: false + default: "" + +permissions: + contents: read + id-token: write + +env: + PROJECT_ID: "basicblock" + GAR_LOCATION: "us-central1" + GAR_REPOSITORY: "docker-images" + WORKLOAD_IDENTITY_PROVIDER: "projects/327281795986/locations/global/workloadIdentityPools/github-pool/providers/basicblock-repo" + SERVICE_ACCOUNT_EMAIL: "githubactions@basicblock.iam.gserviceaccount.com" + +jobs: + publish-webapp: + name: "Build and push webapp" + runs-on: ubuntu-latest + env: + PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING: 1 + steps: + - name: ⬇️ Checkout repo + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: "#️⃣ Get image tag" + id: get_tag + uses: ./.github/actions/get-image-tag + with: + tag: ${{ inputs.image_tag }} + + - name: 📛 Set tags to push + id: set_tags + run: | + ref_without_tag=${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.GAR_REPOSITORY }}/trigger.dev + image_tags=$ref_without_tag:${{ steps.get_tag.outputs.tag }} + + if [[ "${{ steps.get_tag.outputs.is_semver }}" == true ]]; then + image_tags=$image_tags,$ref_without_tag:v4-beta + fi + + echo "image_tags=${image_tags}" >> "$GITHUB_OUTPUT" + + - name: 📝 Set build info + id: set_build_info + run: | + tag=${{ steps.get_tag.outputs.tag }} + if [[ "${{ steps.get_tag.outputs.is_semver }}" == true ]]; then + echo "BUILD_APP_VERSION=${tag}" >> "$GITHUB_OUTPUT" + fi + echo "BUILD_GIT_SHA=${{ github.sha }}" >> "$GITHUB_OUTPUT" + echo "BUILD_GIT_REF_NAME=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" + echo "BUILD_TIMESTAMP_SECONDS=$(date +%s)" >> "$GITHUB_OUTPUT" + + - name: 'Authenticate to Google Cloud' + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: access_token + service_account: ${{ env.SERVICE_ACCOUNT_EMAIL }} + workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }} + project_id: ${{ env.PROJECT_ID }} + + - name: 'Docker auth' + uses: docker/login-action@v3 + with: + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + registry: ${{ env.GAR_LOCATION }}-docker.pkg.dev + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: 🐳 Build image and push to Artifact Registry + uses: docker/build-push-action@v6 + with: + context: . + file: ./docker/Dockerfile + platforms: linux/amd64 + tags: ${{ steps.set_tags.outputs.image_tags }} + push: true + build-args: | + BUILD_APP_VERSION=${{ steps.set_build_info.outputs.BUILD_APP_VERSION }} + BUILD_GIT_SHA=${{ steps.set_build_info.outputs.BUILD_GIT_SHA }} + BUILD_GIT_REF_NAME=${{ steps.set_build_info.outputs.BUILD_GIT_REF_NAME }} + BUILD_TIMESTAMP_SECONDS=${{ steps.set_build_info.outputs.BUILD_TIMESTAMP_SECONDS }} + SENTRY_RELEASE=${{ steps.set_build_info.outputs.BUILD_GIT_SHA }} + SENTRY_ORG=triggerdev + SENTRY_PROJECT=trigger-cloud + secrets: | + sentry_auth_token= + + publish-workers: + name: "Build and push ${{ matrix.package }}" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + package: [coordinator, docker-provider, kubernetes-provider, supervisor] + steps: + - name: ⬇️ Checkout repo + uses: actions/checkout@v4 + + - name: "#️⃣ Get image tag" + id: get_tag + uses: ./.github/actions/get-image-tag + with: + tag: ${{ inputs.image_tag }} + + - name: 📦 Get image repo + id: get_repository + run: | + if [[ "${{ matrix.package }}" == *-provider ]]; then + provider_type=$(echo "${{ matrix.package }}" | cut -d- -f1) + repo=provider/${provider_type} + else + repo="${{ matrix.package }}" + fi + echo "repo=${repo}" >> "$GITHUB_OUTPUT" + + - name: 📛 Set tags to push + id: set_tags + run: | + ref_without_tag=${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.GAR_REPOSITORY }}/${{ steps.get_repository.outputs.repo }} + image_tags=$ref_without_tag:${{ steps.get_tag.outputs.tag }} + + if [[ "${{ matrix.package }}" == "supervisor" && "${{ steps.get_tag.outputs.is_semver }}" == true ]]; then + image_tags=$image_tags,$ref_without_tag:v4-beta + fi + + echo "image_tags=${image_tags}" >> "$GITHUB_OUTPUT" + + - name: 'Authenticate to Google Cloud' + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: access_token + service_account: ${{ env.SERVICE_ACCOUNT_EMAIL }} + workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }} + project_id: ${{ env.PROJECT_ID }} + + - name: 'Docker auth' + uses: docker/login-action@v3 + with: + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + registry: ${{ env.GAR_LOCATION }}-docker.pkg.dev + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: 🐳 Build image and push to Artifact Registry + uses: docker/build-push-action@v6 + with: + context: . + file: ./apps/${{ matrix.package }}/Containerfile + platforms: linux/amd64 + tags: ${{ steps.set_tags.outputs.image_tags }} + push: true diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6213499c5a..b5b3e3c45d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,6 +22,7 @@ on: - ".github/workflows/e2e.yml" - ".github/workflows/publish-webapp.yml" - ".github/workflows/publish-worker.yml" + - ".github/workflows/publish-gcp-images.yml" - "packages/**" - "!packages/**/*.md" - "!packages/**/*.eslintrc" @@ -76,3 +77,10 @@ jobs: secrets: inherit with: image_tag: ${{ inputs.image_tag }} + + publish-gcp-images: + needs: [typecheck, units] + uses: ./.github/workflows/publish-gcp-images.yml + secrets: inherit + with: + image_tag: ${{ inputs.image_tag }} diff --git a/packages/cli-v3/src/build/extensions.ts b/packages/cli-v3/src/build/extensions.ts index e38fe903d8..9ad544f128 100644 --- a/packages/cli-v3/src/build/extensions.ts +++ b/packages/cli-v3/src/build/extensions.ts @@ -208,6 +208,10 @@ function applyLayerToManifest(layer: BuildLayer, manifest: BuildManifest): Build $manifest.image.pkgs = $manifest.image.pkgs.concat(layer.image.pkgs); $manifest.image.pkgs = Array.from(new Set($manifest.image.pkgs)); } + + if (layer.image.entrypointPrefix) { + $manifest.image.entrypointPrefix = [...layer.image.entrypointPrefix]; + } } if (layer.conditions) { diff --git a/packages/cli-v3/src/deploy/buildImage.ts b/packages/cli-v3/src/deploy/buildImage.ts index 2225d7db05..26247787dd 100644 --- a/packages/cli-v3/src/deploy/buildImage.ts +++ b/packages/cli-v3/src/deploy/buildImage.ts @@ -727,14 +727,26 @@ const parseGenerateOptions = (options: GenerateContainerfileOptions) => { baseInstructions, buildArgs, buildEnvVars, + entrypointPrefix: options.image?.entrypointPrefix ?? [], packages, postInstallCommands, }; }; +function serializeEntrypoint(entrypointPrefix: string[], entrypoint: string) { + return JSON.stringify(["dumb-init", ...entrypointPrefix, "node", entrypoint]); +} + async function generateBunContainerfile(options: GenerateContainerfileOptions) { - const { baseImage, buildArgs, buildEnvVars, postInstallCommands, baseInstructions, packages } = - parseGenerateOptions(options); + const { + baseImage, + buildArgs, + buildEnvVars, + entrypointPrefix, + postInstallCommands, + baseInstructions, + packages, + } = parseGenerateOptions(options); return `# syntax=docker/dockerfile:1 # check=skip=SecretsUsedInArgOrEnv @@ -829,14 +841,21 @@ COPY --from=build --chown=bun:bun /app ./ # Copy the index.json file from the indexer stage COPY --from=indexer --chown=bun:bun /app/index.json ./ -ENTRYPOINT [ "dumb-init", "node", "${options.entrypoint}" ] +ENTRYPOINT ${serializeEntrypoint(entrypointPrefix, options.entrypoint)} CMD [] `; } async function generateNodeContainerfile(options: GenerateContainerfileOptions) { - const { baseImage, buildArgs, buildEnvVars, postInstallCommands, baseInstructions, packages } = - parseGenerateOptions(options); + const { + baseImage, + buildArgs, + buildEnvVars, + entrypointPrefix, + postInstallCommands, + baseInstructions, + packages, + } = parseGenerateOptions(options); return `# syntax=docker/dockerfile:1 # check=skip=SecretsUsedInArgOrEnv @@ -939,7 +958,7 @@ COPY --from=build --chown=node:node /app ./ # Copy the index.json file from the indexer stage COPY --from=indexer --chown=node:node /app/index.json ./ -ENTRYPOINT [ "dumb-init", "node", "${options.entrypoint}" ] +ENTRYPOINT ${serializeEntrypoint(entrypointPrefix, options.entrypoint)} CMD [] `; } diff --git a/packages/cli-v3/src/entryPoints/managed-run-controller.ts b/packages/cli-v3/src/entryPoints/managed-run-controller.ts index 212dc6a19c..4baa701b05 100644 --- a/packages/cli-v3/src/entryPoints/managed-run-controller.ts +++ b/packages/cli-v3/src/entryPoints/managed-run-controller.ts @@ -2,9 +2,6 @@ import { env as stdEnv } from "std-env"; import { readJSONFile } from "../utilities/fileSystem.js"; import { WorkerManifest } from "@trigger.dev/core/v3"; import { ManagedRunController } from "./managed/controller.js"; -import { logger } from "../utilities/logger.js"; - -logger.loggerLevel = "debug"; const manifest = await readJSONFile("./index.json"); const workerManifest = WorkerManifest.parse(manifest); diff --git a/packages/core/src/v3/build/extensions.ts b/packages/core/src/v3/build/extensions.ts index 3b809040ea..341af08f80 100644 --- a/packages/core/src/v3/build/extensions.ts +++ b/packages/core/src/v3/build/extensions.ts @@ -56,6 +56,7 @@ export interface BuildLayer { image?: { pkgs?: string[]; instructions?: string[]; + entrypointPrefix?: string[]; }; build?: { env?: Record; diff --git a/packages/core/src/v3/schemas/build.ts b/packages/core/src/v3/schemas/build.ts index 8eb97f0f35..eb0d5dd121 100644 --- a/packages/core/src/v3/schemas/build.ts +++ b/packages/core/src/v3/schemas/build.ts @@ -60,6 +60,7 @@ export const BuildManifest = z.object({ .object({ pkgs: z.array(z.string()).optional(), instructions: z.array(z.string()).optional(), + entrypointPrefix: z.array(z.string()).optional(), }) .optional(), otelImportHook: z