diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a478677..1964008 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,7 @@ on: - "v*" permissions: + id-token: write contents: write jobs: @@ -44,39 +45,70 @@ jobs: name: busybox_${{ steps.version.outputs.tag }}_${{ matrix.arch }} path: builds/${{ steps.version.outputs.busybox_version }}/${{ matrix.arch }}/busybox - release: - name: Release + publish: + name: Publish needs: build runs-on: ubuntu-24.04 steps: - name: Extract version from tag id: version - run: echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" + run: | + TAG="${GITHUB_REF_NAME}" + BUSYBOX_VERSION="${TAG#v}" + BUSYBOX_VERSION="${BUSYBOX_VERSION%%-*}" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "busybox_version=${BUSYBOX_VERSION}" >> "$GITHUB_OUTPUT" - name: Download artifacts uses: actions/download-artifact@v5 with: path: artifacts - - name: Prepare release + - name: Prepare GCS upload + run: | + # Restructure to {version}/{arch}/busybox with SHA256 sidecar for GCS bucket layout + VERSION="${{ steps.version.outputs.busybox_version }}" + mkdir -p "gcs-upload/${VERSION}/amd64" "gcs-upload/${VERSION}/arm64" + for dir in artifacts/busybox_*; do + arch="${dir##*_}" + cp "$dir/busybox" "gcs-upload/${VERSION}/${arch}/busybox" + chmod +x "gcs-upload/${VERSION}/${arch}/busybox" + # Generate SHA256 sidecar for downstream verification + (cd "gcs-upload/${VERSION}/${arch}" && sha256sum busybox > busybox.sha256) + done + ls -laR gcs-upload/ + + - name: Setup GCP auth + id: gcp-auth + uses: google-github-actions/auth@v3 + continue-on-error: true + with: + workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} + + - name: Upload to GCS + if: steps.gcp-auth.outcome == 'success' + uses: google-github-actions/upload-cloud-storage@v3 + continue-on-error: true + with: + path: gcs-upload + destination: ${{ vars.GCP_BUCKET_NAME }}/busybox + gzip: false + parent: false + + - name: Prepare GitHub release run: | TAG="${{ steps.version.outputs.tag }}" mkdir release for dir in artifacts/busybox_*; do - # dir name: busybox_v1.36.1-20260401_amd64 arch="${dir##*_}" cp "$dir/busybox" "release/busybox_${TAG}_${arch}" chmod +x "release/busybox_${TAG}_${arch}" done - ls -la release/ - for f in release/*; do - echo "$(basename "$f"): $(file "$f" | cut -d: -f2)" - done - # Generate checksums file for downstream verification cd release && sha256sum busybox_* > SHA256SUMS cat SHA256SUMS - - name: Create release + - name: Create GitHub release uses: softprops/action-gh-release@v2 with: name: BusyBox ${{ steps.version.outputs.tag }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..135278c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +.tfplan +.terraform diff --git a/terraform/.env.template b/terraform/.env.template new file mode 100644 index 0000000..3451ef6 --- /dev/null +++ b/terraform/.env.template @@ -0,0 +1,2 @@ +GCP_PROJECT_ID= +PREFIX= diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..d896505 --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,44 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/google" { + version = "5.6.0" + constraints = "5.6.0" + hashes = [ + "h1:qhj3soOCQLWWtz4PRZp68q0sJ1F7+JSCBhGualLOEjk=", + "zh:102b6a2672fade82114eb14ed46923fb1b74be2aaca3a50b4f35f7057a9a94b9", + "zh:1a56b63175068c67efbe7d130986ba2839a938f5ffc96a14fd450153174dbfa3", + "zh:1ba1c5e0c86e8aaa8037406390846e78c89b63faf9e527c7874641f35d436e1b", + "zh:3f7161b9288b47cbe89d2f9675f78d83b58ad5880c793b01f50a71ee2583844b", + "zh:66912d6e4180dac37185d17424b345a9d4e3c3c791d45e0737b35e32c9536b35", + "zh:6f06f56e9fac2e55b50e74ffac42d9522bb379394e51dca1eddd4c3b7a68545c", + "zh:8741861ebfa13bb1ed74ea7f4865388a0725ca3a781b6d873ce45e6a4630fe41", + "zh:ae89a9c538665fbc30bb83aa3b13acb18d8380e551ccf242e1c0ab4d626089ab", + "zh:c510f8321c7599aa601b1870fdc0c76cbad3054ed5cc70fe8e37a13a8046a71f", + "zh:cf143a53d5a25c6216d09a9c0b115bb473ffcebd5c4c62b2b2594b1ebc13e662", + "zh:de05b957e5dfdbaf92db47cd9b3ef46a0f8d94599eea6d472928f33058856add", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/integrations/github" { + version = "5.42.0" + constraints = "5.42.0" + hashes = [ + "h1:rfyLEgbZCk3MMCBuGd4PNFM914vtLqGIYcsmVKr6tdg=", + "zh:0f97039c6b70295c4a82347bc8a0bcea700b3fb3df0e0be53585da025584bb7c", + "zh:12e78898580cc2a72b5f2a77e191b158f88e974b0500489b691f34842288745c", + "zh:23660933e4f00293c0d4d6cd6b4d72e382c0df46b70cecf22b5c4c090d3b61e3", + "zh:74119174b46d8d197dd209a246bf8b5db113c66467e02c831e68a8ceea312d3e", + "zh:829c4c0c202fc646eb0e1759eb9c8f0757df5295be2d3344b8fd6ca8ce9ef33b", + "zh:92043e667f520aee4e08a10a183ad5abe5487f3e9c8ad5a55ea1358b14b17b1a", + "zh:998909806b4ff42cf480fcd359ec1f12b868846f89284b991987f55de24876b7", + "zh:9f758447db3bf386516562abd6da1e54d22ddc207bda25961d2b5b049f32da0f", + "zh:a6259215612d4d6a281c671b2d5aa3a0a0b0a3ae92ed60b633998bb692e922d3", + "zh:ad7d78056beb44191911db9443bf5eec41a3d60e7b01def2a9e608d1c4288d27", + "zh:b697e7b0abef3000e1db482c897b82cd455621b488bb6c4cd3d270763d7b08ac", + "zh:db8e849eded8aebff780f89ab7e1339053d2f15c1c8f94103d70266a090527ad", + "zh:e5bdbb85fb148dd75877a7b94b595d4e8680e495c241db02c4b12b91e9d08953", + "zh:ee812c5fd77d3817fb688f720e5eb42d7ff04db67a125de48b05458c9f657483", + ] +} diff --git a/terraform/Makefile b/terraform/Makefile new file mode 100644 index 0000000..dbf26e5 --- /dev/null +++ b/terraform/Makefile @@ -0,0 +1,30 @@ +-include .env + +TERRAFORM_STATE_BUCKET ?= $(GCP_PROJECT_ID)-terraform-state + +tf_vars := TF_VAR_gcp_project_id=$(GCP_PROJECT_ID) \ + TF_VAR_prefix=$(PREFIX) + + +.PHONY: init +init: + @ printf "Initializing Terraform\n\n" + terraform init -reconfigure -input=false -backend-config="bucket=${TERRAFORM_STATE_BUCKET}" + +.PHONY: plan +plan: + @ printf "Planning Terraform\n\n" + terraform fmt -recursive + $(tf_vars) terraform plan -out=.tfplan -compact-warnings -detailed-exitcode + +.PHONY: apply +apply: + @ printf "Applying Terraform\n\n" + $(tf_vars) \ + terraform apply \ + -auto-approve \ + -input=false \ + -compact-warnings \ + -parallelism=20 \ + .tfplan + @ rm .tfplan diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..db17977 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,91 @@ +terraform { + required_version = ">= 1.5.0, < 1.6.0" + backend "gcs" { + prefix = "terraform/fc-busybox-github/state" + } + required_providers { + google = { + source = "hashicorp/google" + version = "5.6.0" + } + github = { + source = "integrations/github" + version = "5.42.0" + } + } +} + +provider "google" { + project = var.gcp_project_id +} + +data "google_project" "gcp_project" {} + +// Workload Identity Federation for GitHub Actions +resource "google_iam_workload_identity_pool" "github_actions_deployment" { + workload_identity_pool_id = "${var.prefix}gha-fc-busybox" + display_name = "GHA for ${var.github_repository} FC BusyBox" + description = "OIDC identity pool for build FC BusyBox ${var.github_repository} via GitHub Actions" +} + +resource "google_iam_workload_identity_pool_provider" "gha_identity_pool_provider" { + workload_identity_pool_id = google_iam_workload_identity_pool.github_actions_deployment.workload_identity_pool_id + workload_identity_pool_provider_id = "${var.prefix}gh-provider" + display_name = "E2B GHA identity pool provider" + attribute_mapping = { + "google.subject" = "assertion.sub" + "attribute.actor" = "assertion.actor" + "attribute.repository" = "assertion.repository" + "attribute.repository_owner" = "assertion.repository_owner" + } + attribute_condition = "assertion.repository == \"${var.github_organization}/${var.github_repository}\"" + + oidc { + issuer_uri = "https://token.actions.githubusercontent.com" + } +} + +resource "google_service_account" "fc_busybox" { + account_id = "${var.prefix}fc-busybox" + display_name = "Service account for ${var.github_repository} FC BusyBox" +} + +resource "google_storage_bucket_iam_member" "fc_busybox_bucket_iam" { + bucket = var.gcs_bucket_name + role = "roles/storage.objectUser" + member = "serviceAccount:${google_service_account.fc_busybox.email}" +} + +resource "google_service_account_iam_member" "gha_service_account_wif_tokencreator_iam_member" { + service_account_id = google_service_account.fc_busybox.name + role = "roles/iam.workloadIdentityUser" + member = "principalSet://iam.googleapis.com/projects/${data.google_project.gcp_project.number}/locations/global/workloadIdentityPools/${google_iam_workload_identity_pool.github_actions_deployment.workload_identity_pool_id}/attribute.repository/${var.github_organization}/${var.github_repository}" +} + +// GitHub secrets and variables +data "google_secret_manager_secret_version" "github_token" { + secret = "${var.prefix}github-repo-token" +} + +provider "github" { + owner = var.github_organization + token = data.google_secret_manager_secret_version.github_token.secret_data +} + +resource "github_actions_secret" "workload_identity_provider_secret" { + repository = var.github_repository + secret_name = "GCP_WORKLOAD_IDENTITY_PROVIDER" + plaintext_value = "projects/${data.google_project.gcp_project.number}/locations/global/workloadIdentityPools/${google_iam_workload_identity_pool.github_actions_deployment.workload_identity_pool_id}/providers/${google_iam_workload_identity_pool_provider.gha_identity_pool_provider.workload_identity_pool_provider_id}" +} + +resource "github_actions_secret" "service_account_email_secret" { + repository = var.github_repository + secret_name = "GCP_SERVICE_ACCOUNT_EMAIL" + plaintext_value = google_service_account.fc_busybox.email +} + +resource "github_actions_variable" "gcs_bucket_name" { + repository = var.github_repository + value = var.gcs_bucket_name + variable_name = "GCP_BUCKET_NAME" +} diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..e33faf4 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,25 @@ +variable "github_organization" { + type = string + default = "e2b-dev" +} + +variable "github_repository" { + type = string + default = "fc-busybox" +} + +variable "gcp_project_id" { + description = "The project to deploy the cluster in" + type = string +} + +variable "prefix" { + description = "The prefix to use for all resources in this module" + type = string +} + +variable "gcs_bucket_name" { + description = "The name of the GCS bucket to store the busybox binaries" + type = string + default = "e2b-prod-public-builds" +}