diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 6bfa4172..00000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,167 +0,0 @@ -name: Deploy to Production - -permissions: - contents: read - -on: - workflow_dispatch: - -jobs: - release: - runs-on: ubuntu-latest - environment: main - permissions: - contents: write - id-token: write - defaults: - run: - working-directory: ${{ github.workspace }} - env: - NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - steps: - - name: Checkout code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - fetch-depth: 0 - token: ${{ secrets.RELEASE_TOKEN }} - - - name: Configure Git - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - - name: Install Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 - with: - node-version-file: '.nvmrc' - - - name: Install pnpm - uses: pnpm/action-setup@c5ba7f7862a0f64c1b1a05fbac13e0b8e86ba08c # v4 - - - name: Get pnpm store directory - shell: bash - run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - name: Cache pnpm store - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Graduate Release - run: pnpm graduate - env: - GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} - - - name: Push Release Commit and Tags - run: | - git push - git push --tags - - - name: Get short commit hash - run: | - echo "short_commit_sha=$(git rev-parse --short=8 HEAD)" >> $GITHUB_ENV - - - name: Authenticate to Google Cloud - id: 'auth' - uses: 'google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed' # v2.1.13 - with: - workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} - service_account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} - - - name: Set up Cloud SDK - uses: 'google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f' # v2.2.1 - - - name: Install gh CLI - run: | - type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y) - curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ - && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ - && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ - && sudo apt update \ - && sudo apt install gh -y - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@885d1462b80bc1c1c7f0b00334ad271f09369c55 # v2 - - - name: Generate Prisma client - run: pnpm prisma:generate:postgres - - - name: Build all projects - run: | - NX_BRANCH=${GITHUB_REF#refs/heads/} pnpm nx run-many --target=build --all=true --parallel=3 - NX_BRANCH=${GITHUB_REF#refs/heads/} pnpm nx run-many --target=post-build --all=true --parallel=3 - - - name: Set up Terraform - uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 # v2 - with: - terraform_version: 1.7.5 - - - name: Terraform init - run: | - echo "Running terraform init..." - echo "" - terraform init - working-directory: teams/kernel/iac/production - - - name: Terraform validate - run: | - echo "Running terraform validate..." - terraform validate - working-directory: teams/kernel/iac/production - - - name: Terraform Plan - env: - TF_VAR_domain_name: ${{ secrets.DOMAIN_NAME }} - TF_VAR_gcp_project_id: ${{ secrets.GCP_PROJECT_ID }} - TF_VAR_gcp_location: ${{ secrets.GCP_LOCATION }} - TF_VAR_short_commit_sha: ${{ env.short_commit_sha }} - TF_VAR_support_account_email: ${{ secrets.SUPPORT_ACCOUNT_EMAIL }} - TF_VAR_owner_account_email: ${{ secrets.OWNER_ACCOUNT_EMAIL }} - TF_VAR_gcp_billing_account_id: ${{ secrets.GCP_BILLING_ACCOUNT_ID }} - TF_VAR_gcp_organization_id: ${{ secrets.GCP_ORGANIZATION_ID }} - TF_VAR_nx_cloud_access_token: ${{ env.NX_CLOUD_ACCESS_TOKEN }} - TF_VAR_neon_api_key: ${{ secrets.NEON_API_KEY }} - TF_VAR_neon_project_location: ${{ secrets.NEON_PROJECT_LOCATION }} - TF_VAR_unleash_api_url: ${{ secrets.UNLEASH_API_URL }} - TF_VAR_unleash_auth_token: ${{ secrets.UNLEASH_AUTH_TOKEN }} - TF_VAR_environment_path: ${{ env.GITHUB_ENV }} - TF_VAR_mongodb_atlas_org_id: ${{ secrets.MONGODB_ATLAS_ORG_ID }} - TF_VAR_mongodb_atlas_private_key: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} - TF_VAR_mongodb_atlas_public_key: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} - TF_VAR_service_account_email: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} - run: | - echo "Running terraform plan..." - echo "Commit Hash: ${{ env.short_commit_sha }}" - terraform plan -out=tfplan - working-directory: teams/kernel/iac/production - - - name: Terraform Apply - run: | - echo "Running terraform apply..." - terraform apply -auto-approve tfplan - working-directory: teams/kernel/iac/production - - - name: Emmit Compass Deployment event - if: false # Intentionally disabled - env: - ATLASSIAN_DOMAIN: ${{ secrets.ATLASSIAN_DOMAIN }} - ATLASSIAN_CLOUD_ID: ${{ secrets.ATLASSIAN_CLOUD_ID }} - ATLASSIAN_USER_EMAIL: ${{ secrets.ATLASSIAN_USER_EMAIL }} - ATLASSIAN_USER_API_TOKEN: ${{ secrets.ATLASSIAN_USER_API_TOKEN }} - COMPASS_EXTERNAL_EVENT_SOURCE_ID: ${{ secrets.COMPASS_EXTERNAL_EVENT_SOURCE_ID }} - run: | - bash scripts/compass/emmit-deployment-event.sh \ - --atlassian-domain="$ATLASSIAN_DOMAIN" \ - --atlassian-cloud-id="$ATLASSIAN_CLOUD_ID" \ - --atlassian-user-email="$ATLASSIAN_USER_EMAIL" \ - --atlassian-user-api-token="$ATLASSIAN_USER_API_TOKEN" \ - --compass-external-event-source-id="$COMPASS_EXTERNAL_EVENT_SOURCE_ID" \ - --pipeline-run-id="$GITHUB_RUN_ID" \ - --repository-name="$GITHUB_REPOSITORY" diff --git a/.github/workflows/teams-kernel-workflows-bootstrap-team.yml b/.github/workflows/teams-kernel-workflows-bootstrap-team.yml new file mode 100644 index 00000000..4bdc4548 --- /dev/null +++ b/.github/workflows/teams-kernel-workflows-bootstrap-team.yml @@ -0,0 +1,105 @@ +name: Bootstrap Team Infrastructure (Reusable) + +on: + workflow_call: + inputs: + team-name: + required: true + type: string + description: 'Team name (kernel, people, things)' + secrets: + GCP_WORKLOAD_IDENTITY_PROVIDER: + required: true + GCP_SERVICE_ACCOUNT_EMAIL: + required: true + OWNER_ACCOUNT_EMAIL: + required: true + GCP_ORGANIZATION_ID: + required: true + GCP_BILLING_ACCOUNT_ID: + required: true + DOMAIN_NAME: + required: true + GITHUB_USERNAME: + required: true + NEON_API_KEY: + required: true + NEON_PROJECT_LOCATION: + required: true + MONGODB_ATLAS_ORG_ID: + required: true + MONGODB_ATLAS_PUBLIC_KEY: + required: true + MONGODB_ATLAS_PRIVATE_KEY: + required: true + NX_CLOUD_ACCESS_TOKEN_READ_WRITE: + required: true + NX_CLOUD_ACCESS_TOKEN_READ: + required: true + +jobs: + bootstrap: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + + - name: Authenticate to Google Cloud + id: auth + uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2.1.13 + with: + workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f # v2.2.1 + + - name: Install gh CLI + run: | + type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y) + curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ + && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ + && sudo apt update \ + && sudo apt install gh -y + + - name: Run bootstrap script for ${{ inputs.team-name }} + env: + OWNER_ACCOUNT_EMAIL: ${{ secrets.OWNER_ACCOUNT_EMAIL }} + GCP_ORGANIZATION_ID: ${{ secrets.GCP_ORGANIZATION_ID }} + GCP_BILLING_ACCOUNT_ID: ${{ secrets.GCP_BILLING_ACCOUNT_ID }} + DOMAIN_NAME: ${{ secrets.DOMAIN_NAME }} + GITHUB_USERNAME: ${{ secrets.GITHUB_USERNAME }} + NEON_API_KEY: ${{ secrets.NEON_API_KEY }} + NEON_PROJECT_LOCATION: ${{ secrets.NEON_PROJECT_LOCATION }} + MONGODB_ATLAS_ORG_ID: ${{ secrets.MONGODB_ATLAS_ORG_ID }} + MONGODB_ATLAS_PUBLIC_KEY: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} + MONGODB_ATLAS_PRIVATE_KEY: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} + NX_CLOUD_ACCESS_TOKEN_READ_WRITE: ${{ secrets.NX_CLOUD_ACCESS_TOKEN_READ_WRITE }} + NX_CLOUD_ACCESS_TOKEN_READ: ${{ secrets.NX_CLOUD_ACCESS_TOKEN_READ }} + run: | + echo "Bootstrap: Starting ${{ inputs.team-name }} team infrastructure bootstrap" + sh teams/kernel/iac/bootstrap/project-setup.sh \ + --owner-account-email="$OWNER_ACCOUNT_EMAIL" \ + --gcp-organization-id="$GCP_ORGANIZATION_ID" \ + --gcp-billing-account-id="$GCP_BILLING_ACCOUNT_ID" \ + --domain-name="$DOMAIN_NAME" \ + --github-username="$GITHUB_USERNAME" \ + --github-repository="${{ github.repository }}" \ + --neon-api-key="$NEON_API_KEY" \ + --neon-project-location="$NEON_PROJECT_LOCATION" \ + --mongodb-atlas-org-id="$MONGODB_ATLAS_ORG_ID" \ + --mongodb-atlas-public-key="$MONGODB_ATLAS_PUBLIC_KEY" \ + --mongodb-atlas-private-key="$MONGODB_ATLAS_PRIVATE_KEY" \ + --nx-cloud-access-token-read-write="$NX_CLOUD_ACCESS_TOKEN_READ_WRITE" \ + --nx-cloud-access-token-read="$NX_CLOUD_ACCESS_TOKEN_READ" + echo "Bootstrap: Completed ${{ inputs.team-name }} team infrastructure bootstrap" + + - name: Verify bootstrap success + run: | + echo "Bootstrap: Verifying ${{ inputs.team-name }} infrastructure" + gcloud projects list --filter="name:*${{ inputs.team-name }}*" --format="table(name,projectId)" diff --git a/.github/workflows/teams-kernel-workflows-bootstrap.yml b/.github/workflows/teams-kernel-workflows-bootstrap.yml new file mode 100644 index 00000000..8595e926 --- /dev/null +++ b/.github/workflows/teams-kernel-workflows-bootstrap.yml @@ -0,0 +1,100 @@ +name: Bootstrap All Team Infrastructure + +on: + workflow_dispatch: + inputs: + teams: + description: 'Teams to bootstrap (all, kernel, people, things, or space-separated list)' + required: false + default: 'all' + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + bootstrap-kernel: + name: Bootstrap Kernel Team + uses: ./.github/workflows/teams-kernel-workflows-bootstrap-team.yml + permissions: + contents: read + if: | + github.event.inputs.teams == 'all' || + github.event.inputs.teams == 'kernel' || + contains(github.event.inputs.teams, 'kernel') + secrets: + GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + GCP_SERVICE_ACCOUNT_EMAIL: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} + OWNER_ACCOUNT_EMAIL: ${{ secrets.OWNER_ACCOUNT_EMAIL }} + GCP_ORGANIZATION_ID: ${{ secrets.GCP_ORGANIZATION_ID }} + GCP_BILLING_ACCOUNT_ID: ${{ secrets.GCP_BILLING_ACCOUNT_ID }} + DOMAIN_NAME: ${{ secrets.DOMAIN_NAME }} + GITHUB_USERNAME: ${{ secrets.GITHUB_USERNAME }} + NEON_API_KEY: ${{ secrets.NEON_API_KEY }} + NEON_PROJECT_LOCATION: ${{ secrets.NEON_PROJECT_LOCATION }} + MONGODB_ATLAS_ORG_ID: ${{ secrets.MONGODB_ATLAS_ORG_ID }} + MONGODB_ATLAS_PUBLIC_KEY: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} + MONGODB_ATLAS_PRIVATE_KEY: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} + NX_CLOUD_ACCESS_TOKEN_READ_WRITE: ${{ secrets.NX_CLOUD_ACCESS_TOKEN_READ_WRITE }} + NX_CLOUD_ACCESS_TOKEN_READ: ${{ secrets.NX_CLOUD_ACCESS_TOKEN_READ }} + with: + team-name: kernel + + bootstrap-people: + name: Bootstrap People Team + uses: ./.github/workflows/teams-kernel-workflows-bootstrap-team.yml + needs: bootstrap-kernel + permissions: + contents: read + if: | + needs.bootstrap-kernel.result == 'success' && ( + github.event.inputs.teams == 'all' || + github.event.inputs.teams == 'people' || + contains(github.event.inputs.teams, 'people') + ) + secrets: + GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + GCP_SERVICE_ACCOUNT_EMAIL: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} + OWNER_ACCOUNT_EMAIL: ${{ secrets.OWNER_ACCOUNT_EMAIL }} + GCP_ORGANIZATION_ID: ${{ secrets.GCP_ORGANIZATION_ID }} + GCP_BILLING_ACCOUNT_ID: ${{ secrets.GCP_BILLING_ACCOUNT_ID }} + DOMAIN_NAME: ${{ secrets.DOMAIN_NAME }} + GITHUB_USERNAME: ${{ secrets.GITHUB_USERNAME }} + NEON_API_KEY: ${{ secrets.NEON_API_KEY }} + NEON_PROJECT_LOCATION: ${{ secrets.NEON_PROJECT_LOCATION }} + MONGODB_ATLAS_ORG_ID: ${{ secrets.MONGODB_ATLAS_ORG_ID }} + MONGODB_ATLAS_PUBLIC_KEY: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} + MONGODB_ATLAS_PRIVATE_KEY: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} + NX_CLOUD_ACCESS_TOKEN_READ_WRITE: ${{ secrets.NX_CLOUD_ACCESS_TOKEN_READ_WRITE }} + NX_CLOUD_ACCESS_TOKEN_READ: ${{ secrets.NX_CLOUD_ACCESS_TOKEN_READ }} + with: + team-name: people + + bootstrap-things: + name: Bootstrap Things Team + uses: ./.github/workflows/teams-kernel-workflows-bootstrap-team.yml + needs: bootstrap-kernel + permissions: + contents: read + if: | + needs.bootstrap-kernel.result == 'success' && ( + github.event.inputs.teams == 'all' || + github.event.inputs.teams == 'things' || + contains(github.event.inputs.teams, 'things') + ) + secrets: + GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + GCP_SERVICE_ACCOUNT_EMAIL: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} + OWNER_ACCOUNT_EMAIL: ${{ secrets.OWNER_ACCOUNT_EMAIL }} + GCP_ORGANIZATION_ID: ${{ secrets.GCP_ORGANIZATION_ID }} + GCP_BILLING_ACCOUNT_ID: ${{ secrets.GCP_BILLING_ACCOUNT_ID }} + DOMAIN_NAME: ${{ secrets.DOMAIN_NAME }} + GITHUB_USERNAME: ${{ secrets.GITHUB_USERNAME }} + NEON_API_KEY: ${{ secrets.NEON_API_KEY }} + NEON_PROJECT_LOCATION: ${{ secrets.NEON_PROJECT_LOCATION }} + MONGODB_ATLAS_ORG_ID: ${{ secrets.MONGODB_ATLAS_ORG_ID }} + MONGODB_ATLAS_PUBLIC_KEY: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} + MONGODB_ATLAS_PRIVATE_KEY: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} + NX_CLOUD_ACCESS_TOKEN_READ_WRITE: ${{ secrets.NX_CLOUD_ACCESS_TOKEN_READ_WRITE }} + NX_CLOUD_ACCESS_TOKEN_READ: ${{ secrets.NX_CLOUD_ACCESS_TOKEN_READ }} + with: + team-name: things diff --git a/.github/workflows/ci.yml b/.github/workflows/teams-kernel-workflows-ci.yml similarity index 100% rename from .github/workflows/ci.yml rename to .github/workflows/teams-kernel-workflows-ci.yml diff --git a/.github/workflows/teams-kernel-workflows-deploy-team.yml b/.github/workflows/teams-kernel-workflows-deploy-team.yml new file mode 100644 index 00000000..52bd4a1a --- /dev/null +++ b/.github/workflows/teams-kernel-workflows-deploy-team.yml @@ -0,0 +1,150 @@ +name: Deploy Team Infrastructure (Reusable) + +on: + workflow_call: + inputs: + team-name: + required: true + type: string + description: 'Team name (kernel, people, things)' + iac-path: + required: true + type: string + description: 'Path to team IAC directory (e.g., teams/kernel/iac/production)' + secrets: + GCP_WORKLOAD_IDENTITY_PROVIDER: + required: true + GCP_SERVICE_ACCOUNT_EMAIL: + required: true + NX_CLOUD_ACCESS_TOKEN: + required: true + DOMAIN_NAME: + required: false + GCP_PROJECT_ID: + required: false + GCP_LOCATION: + required: false + SUPPORT_ACCOUNT_EMAIL: + required: false + OWNER_ACCOUNT_EMAIL: + required: false + GCP_BILLING_ACCOUNT_ID: + required: false + GCP_ORGANIZATION_ID: + required: false + NEON_API_KEY: + required: false + NEON_PROJECT_LOCATION: + required: false + UNLEASH_API_URL: + required: false + UNLEASH_AUTH_TOKEN: + required: false + MONGODB_ATLAS_ORG_ID: + required: false + MONGODB_ATLAS_PRIVATE_KEY: + required: false + MONGODB_ATLAS_PUBLIC_KEY: + required: false + +jobs: + deploy: + runs-on: ubuntu-latest + environment: main + permissions: + contents: read + id-token: write + + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + fetch-depth: 0 + + - name: Get short commit hash + run: | + echo "short_commit_sha=$(git rev-parse --short=8 HEAD)" >> $GITHUB_ENV + + - name: Install Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version-file: '.nvmrc' + + - name: Install pnpm + uses: pnpm/action-setup@c5ba7f7862a0f64c1b1a05fbac13e0b8e86ba08c # v4 + + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Cache pnpm store + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate Prisma client + run: pnpm prisma:generate:postgres + + - name: Authenticate to Google Cloud + id: auth + uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2.1.13 + with: + workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f # v2.2.1 + + - name: Set up Terraform + uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 # v2 + with: + terraform_version: 1.7.5 + + - name: Terraform init + working-directory: ${{ inputs.iac-path }} + run: | + echo "Running terraform init for ${{ inputs.team-name }}..." + terraform init + + - name: Terraform validate + working-directory: ${{ inputs.iac-path }} + run: | + echo "Running terraform validate for ${{ inputs.team-name }}..." + terraform validate + + - name: Terraform Plan + working-directory: ${{ inputs.iac-path }} + env: + TF_VAR_domain_name: ${{ secrets.DOMAIN_NAME }} + TF_VAR_gcp_project_id: ${{ secrets.GCP_PROJECT_ID }} + TF_VAR_gcp_location: ${{ secrets.GCP_LOCATION }} + TF_VAR_short_commit_sha: ${{ env.short_commit_sha }} + TF_VAR_support_account_email: ${{ secrets.SUPPORT_ACCOUNT_EMAIL }} + TF_VAR_owner_account_email: ${{ secrets.OWNER_ACCOUNT_EMAIL }} + TF_VAR_gcp_billing_account_id: ${{ secrets.GCP_BILLING_ACCOUNT_ID }} + TF_VAR_gcp_organization_id: ${{ secrets.GCP_ORGANIZATION_ID }} + TF_VAR_nx_cloud_access_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + TF_VAR_neon_api_key: ${{ secrets.NEON_API_KEY }} + TF_VAR_neon_project_location: ${{ secrets.NEON_PROJECT_LOCATION }} + TF_VAR_unleash_api_url: ${{ secrets.UNLEASH_API_URL }} + TF_VAR_unleash_auth_token: ${{ secrets.UNLEASH_AUTH_TOKEN }} + TF_VAR_mongodb_atlas_org_id: ${{ secrets.MONGODB_ATLAS_ORG_ID }} + TF_VAR_mongodb_atlas_private_key: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} + TF_VAR_mongodb_atlas_public_key: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} + TF_VAR_service_account_email: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} + run: | + echo "Running terraform plan for ${{ inputs.team-name }}..." + echo "Commit Hash: ${{ env.short_commit_sha }}" + terraform plan -out=tfplan + + - name: Terraform Apply + working-directory: ${{ inputs.iac-path }} + run: | + echo "Running terraform apply for ${{ inputs.team-name }}..." + terraform apply -auto-approve tfplan diff --git a/.github/workflows/teams-kernel-workflows-deploy.yml b/.github/workflows/teams-kernel-workflows-deploy.yml new file mode 100644 index 00000000..d69dea28 --- /dev/null +++ b/.github/workflows/teams-kernel-workflows-deploy.yml @@ -0,0 +1,178 @@ +name: Deploy to Production + +on: + workflow_dispatch: + +jobs: + build: + name: Build and Release + runs-on: ubuntu-latest + environment: main + permissions: + contents: write + id-token: write + defaults: + run: + working-directory: ${{ github.workspace }} + env: + NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + fetch-depth: 0 + token: ${{ secrets.RELEASE_TOKEN }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Install Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version-file: '.nvmrc' + + - name: Install pnpm + uses: pnpm/action-setup@c5ba7f7862a0f64c1b1a05fbac13e0b8e86ba08c # v4 + + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Cache pnpm store + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Graduate Release + run: pnpm graduate + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + + - name: Push Release Commit and Tags + run: | + git push + git push --tags + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@885d1462b80bc1c1c7f0b00334ad271f09369c55 # v2 + + - name: Generate Prisma client + run: pnpm prisma:generate:postgres + + - name: Build all projects + run: | + NX_BRANCH=${GITHUB_REF#refs/heads/} pnpm nx run-many --target=build --all=true --parallel=3 + NX_BRANCH=${GITHUB_REF#refs/heads/} pnpm nx run-many --target=post-build --all=true --parallel=3 + + deploy-kernel: + name: Deploy Kernel Team + permissions: + contents: read + needs: build + uses: ./.github/workflows/teams-kernel-workflows-deploy-team.yml + secrets: + GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + GCP_SERVICE_ACCOUNT_EMAIL: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} + NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + DOMAIN_NAME: ${{ secrets.DOMAIN_NAME }} + GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} + GCP_LOCATION: ${{ secrets.GCP_LOCATION }} + SUPPORT_ACCOUNT_EMAIL: ${{ secrets.SUPPORT_ACCOUNT_EMAIL }} + OWNER_ACCOUNT_EMAIL: ${{ secrets.OWNER_ACCOUNT_EMAIL }} + GCP_BILLING_ACCOUNT_ID: ${{ secrets.GCP_BILLING_ACCOUNT_ID }} + GCP_ORGANIZATION_ID: ${{ secrets.GCP_ORGANIZATION_ID }} + NEON_API_KEY: ${{ secrets.NEON_API_KEY }} + NEON_PROJECT_LOCATION: ${{ secrets.NEON_PROJECT_LOCATION }} + UNLEASH_API_URL: ${{ secrets.UNLEASH_API_URL }} + UNLEASH_AUTH_TOKEN: ${{ secrets.UNLEASH_AUTH_TOKEN }} + MONGODB_ATLAS_ORG_ID: ${{ secrets.MONGODB_ATLAS_ORG_ID }} + MONGODB_ATLAS_PRIVATE_KEY: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} + MONGODB_ATLAS_PUBLIC_KEY: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} + with: + team-name: kernel + iac-path: teams/kernel/iac/production + + deploy-people: + name: Deploy People Team + permissions: + contents: read + needs: deploy-kernel + if: needs.deploy-kernel.result == 'success' + uses: ./.github/workflows/teams-kernel-workflows-deploy-team.yml + secrets: + GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + GCP_SERVICE_ACCOUNT_EMAIL: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} + NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + DOMAIN_NAME: ${{ secrets.DOMAIN_NAME }} + GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} + GCP_LOCATION: ${{ secrets.GCP_LOCATION }} + SUPPORT_ACCOUNT_EMAIL: ${{ secrets.SUPPORT_ACCOUNT_EMAIL }} + OWNER_ACCOUNT_EMAIL: ${{ secrets.OWNER_ACCOUNT_EMAIL }} + GCP_BILLING_ACCOUNT_ID: ${{ secrets.GCP_BILLING_ACCOUNT_ID }} + GCP_ORGANIZATION_ID: ${{ secrets.GCP_ORGANIZATION_ID }} + NEON_API_KEY: ${{ secrets.NEON_API_KEY }} + NEON_PROJECT_LOCATION: ${{ secrets.NEON_PROJECT_LOCATION }} + UNLEASH_API_URL: ${{ secrets.UNLEASH_API_URL }} + UNLEASH_AUTH_TOKEN: ${{ secrets.UNLEASH_AUTH_TOKEN }} + MONGODB_ATLAS_ORG_ID: ${{ secrets.MONGODB_ATLAS_ORG_ID }} + MONGODB_ATLAS_PRIVATE_KEY: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} + MONGODB_ATLAS_PUBLIC_KEY: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} + with: + team-name: people + iac-path: teams/people/iac/production + + deploy-things: + name: Deploy Things Team + needs: deploy-kernel + permissions: + contents: read + if: needs.deploy-kernel.result == 'success' + uses: ./.github/workflows/teams-kernel-workflows-deploy-team.yml + secrets: + GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + GCP_SERVICE_ACCOUNT_EMAIL: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }} + NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + DOMAIN_NAME: ${{ secrets.DOMAIN_NAME }} + GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} + GCP_LOCATION: ${{ secrets.GCP_LOCATION }} + SUPPORT_ACCOUNT_EMAIL: ${{ secrets.SUPPORT_ACCOUNT_EMAIL }} + OWNER_ACCOUNT_EMAIL: ${{ secrets.OWNER_ACCOUNT_EMAIL }} + GCP_BILLING_ACCOUNT_ID: ${{ secrets.GCP_BILLING_ACCOUNT_ID }} + GCP_ORGANIZATION_ID: ${{ secrets.GCP_ORGANIZATION_ID }} + NEON_API_KEY: ${{ secrets.NEON_API_KEY }} + NEON_PROJECT_LOCATION: ${{ secrets.NEON_PROJECT_LOCATION }} + UNLEASH_API_URL: ${{ secrets.UNLEASH_API_URL }} + UNLEASH_AUTH_TOKEN: ${{ secrets.UNLEASH_AUTH_TOKEN }} + MONGODB_ATLAS_ORG_ID: ${{ secrets.MONGODB_ATLAS_ORG_ID }} + MONGODB_ATLAS_PRIVATE_KEY: ${{ secrets.MONGODB_ATLAS_PRIVATE_KEY }} + MONGODB_ATLAS_PUBLIC_KEY: ${{ secrets.MONGODB_ATLAS_PUBLIC_KEY }} + with: + team-name: things + iac-path: teams/things/iac/production + + emit-compass-deployment: + name: Emit Compass Deployment Event + permissions: + contents: read + needs: [deploy-kernel, deploy-people, deploy-things] + if: false # Intentionally disabled + runs-on: ubuntu-latest + steps: + - name: Emit deployment event + env: + ATLASSIAN_DOMAIN: ${{ secrets.ATLASSIAN_DOMAIN }} + ATLASSIAN_CLOUD_ID: ${{ secrets.ATLASSIAN_CLOUD_ID }} + ATLASSIAN_USER_EMAIL: ${{ secrets.ATLASSIAN_USER_EMAIL }} + ATLASSIAN_USER_API_TOKEN: ${{ secrets.ATLASSIAN_USER_API_TOKEN }} + COMPASS_EXTERNAL_EVENT_SOURCE_ID: ${{ secrets.COMPASS_EXTERNAL_EVENT_SOURCE_ID }} + run: | + echo "Deployment complete. Compass event emission intentionally disabled." diff --git a/.github/workflows/pr.yml b/.github/workflows/teams-kernel-workflows-pr.yml similarity index 100% rename from .github/workflows/pr.yml rename to .github/workflows/teams-kernel-workflows-pr.yml diff --git a/.github/workflows/security.yml b/.github/workflows/teams-kernel-workflows-security.yml similarity index 100% rename from .github/workflows/security.yml rename to .github/workflows/teams-kernel-workflows-security.yml diff --git a/docs/insights-long-term.md b/docs/insights-long-term.md index 30ce8045..8c9f1163 100644 --- a/docs/insights-long-term.md +++ b/docs/insights-long-term.md @@ -101,4 +101,4 @@ Key insights from this repository. 97. Chaos engineering reveals resilience gaps through continuous fault injection. 98. Terraform state is managed remotely, never locally. 99. Provider versions are pinned in each module's versions.tf file. -100. Group operations under section headers and suppress verbose CLI output for script debuggability. +100. Reusable workflows + NX dependencies enable independent team infrastructure bootstrap. diff --git a/docs/insights-short-term.md b/docs/insights-short-term.md index b164eea7..1732a255 100644 --- a/docs/insights-short-term.md +++ b/docs/insights-short-term.md @@ -2,6 +2,23 @@ Latest 100 insights derived from recent project activity, newest first. +- [2026-02-15 14:00 UTC] Move secrets from run blocks to env: to enable GitHub masking +- [2026-02-14 14:35 UTC] Combine job needs with needs.X.result == 'success' to enforce deployment success +- [2026-02-14 14:30 UTC] Complete security fixes end-to-end: implement, test, commit, push for compliance +- [2026-02-14 14:15 UTC] Add explicit job permissions after removing workflow-level for least privilege +- [2026-02-14 14:00 UTC] Apply workflow security fixes consistently across similar files to prevent drift +- [2026-02-14 13:45 UTC] Move GitHub Actions permissions from workflow to job level for least privilege +- [2026-02-14 13:15 UTC] Explicit permissions blocks in workflows restrict GITHUB_TOKEN to least privilege +- [2026-02-14 12:30 UTC] Use HEREDOC with gh pr create to preserve PR template formatting and structure +- [2026-02-14 10:00 UTC] Confirm branching strategy before creating PR from uncommitted changes on main +- [2026-02-13 22:30 UTC] Team-owned reusable workflows under teams/*/ with entry points in .github/ for discoverability +- [2026-02-13 21:30 UTC] Explicit script references in workflows beat symlinks; clarity and traceability matter +- [2026-02-13 21:15 UTC] Explicit script paths via env vars avoid symlink indirection; clarity over magic +- [2026-02-13 21:00 UTC] Implicit patterns need explicit documentation; README + metadata prevent confusion +- [2026-02-13 20:45 UTC] Symlinks transparently share shell scripts; lightweight DRY alternative to npm packages +- [2026-02-13 20:15 UTC] Symlinks for shell scripts avoid npm overhead; NX implicit deps document ordering +- [2026-02-13 19:53 UTC] Symlinks + reusable GitHub workflows orchestrate multi-team infrastructure bootstrap +- [2026-02-13 18:45 UTC] Reusable workflows + NX dependencies enable independent team infrastructure bootstrap - [2026-02-13 15:00 UTC] Idempotent logging: use section headers, suppress verbose output, and show status (○/✓) - [2026-02-13 14:00 UTC] Show resource status (created vs existing) in idempotent scripts for debuggability - [2026-02-13 13:00 UTC] Group operations under headers; suppress verbose output to improve script debuggability @@ -85,19 +102,3 @@ Latest 100 insights derived from recent project activity, newest first. - [2026-02-10 09:00 UTC] Separate infra provisioners (Crossplane) from service modules (IAM) so multiple services reuse them - [2026-02-10 08:30 UTC] Two-phase TF apply: targeted `-target` installs CRDs, then full apply plans resources needing them - [2026-02-10 08:00 UTC] Placeholder provider blocks with dummy tokens satisfy TF init for count=0 cloud modules -- [2026-02-10 07:30 UTC] Cloud roots import sub-modules directly; routers can't gate providers with count=0 -- [2026-02-10 07:15 UTC] Versioned environment modules with local/cloud sub-paths enable parallel evolution -- [2026-02-10 07:00 UTC] Terraform `-target` requires full nested module path, not just the leaf module name -- [2026-02-10 06:30 UTC] count=0 sub-modules don't isolate providers; exclude cloud modules from local tree -- [2026-02-10 06:00 UTC] Skip `terraform destroy` for fresh clusters; `minikube delete --profile` suffices alone -- [2026-02-10 05:45 UTC] Terraform configures ALL providers in module tree even when parent has count=0 -- [2026-02-10 05:30 UTC] Nuke local infra (minikube delete) + rm tfstate when destroy fails on missing CRDs -- [2026-02-10 05:15 UTC] `terraform state mv` bypasses moved-block + missing-provider catch-22 locally -- [2026-02-10 05:00 UTC] Terraform `moved` blocks must all resolve before `-target` applies succeed -- [2026-02-10 04:45 UTC] Terraform validates module output attributes at plan time even inside count=0 modules -- [2026-02-10 04:15 UTC] MongoDB Atlas TF provider v2.x changes `replication_specs` from block to argument syntax -- [2026-02-10 03:30 UTC] Wrap cloud-only TF resources in count-gated sub-modules to avoid provider requirements locally -- [2026-02-09 21:45 UTC] NX hides `chore` by default; override via `release.conventionalCommits.types` in nx.json -- [2026-02-09 19:30 UTC] Fresh cluster bootstraps skip incremental upgrade paths; migrate only in-place -- [2026-02-09 19:15 UTC] provider-kubernetes v1.2.0+ requires Crossplane 2.0+; upgrade both together -- [2026-02-09 18:45 UTC] Pinned image versions drift across modules; grep all references before upgrading diff --git a/teams/kernel/iac/bootstrap/package.json b/teams/kernel/iac/bootstrap/package.json index da408623..f2fe76ab 100644 --- a/teams/kernel/iac/bootstrap/package.json +++ b/teams/kernel/iac/bootstrap/package.json @@ -3,5 +3,8 @@ "version": "1.0.0", "description": "Bootstrap infrastructure as code for kernel IAC", "private": true, - "type": "module" + "type": "module", + "nx": { + "implicitDependencies": [] + } } \ No newline at end of file diff --git a/teams/people/iac/bootstrap/package.json b/teams/people/iac/bootstrap/package.json new file mode 100644 index 00000000..a62629a6 --- /dev/null +++ b/teams/people/iac/bootstrap/package.json @@ -0,0 +1,10 @@ +{ + "name": "people-iac-bootstrap", + "version": "1.0.0", + "description": "Bootstrap infrastructure for people team", + "private": true, + "type": "module", + "nx": { + "implicitDependencies": ["kernel-iac-bootstrap"] + } +} diff --git a/teams/things/iac/bootstrap/package.json b/teams/things/iac/bootstrap/package.json new file mode 100644 index 00000000..7d354e7a --- /dev/null +++ b/teams/things/iac/bootstrap/package.json @@ -0,0 +1,10 @@ +{ + "name": "things-iac-bootstrap", + "version": "1.0.0", + "description": "Bootstrap infrastructure for things team", + "private": true, + "type": "module", + "nx": { + "implicitDependencies": ["kernel-iac-bootstrap"] + } +}