diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 848a77c..8f3b1ed 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -11,6 +11,12 @@ jobs: - uses: actions/setup-node@v2 with: node-version: '20.x' + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: '1.14.8' + - name: Install run: npm ci - name: Build diff --git a/.github/workflows/npm-publish-beta.yml b/.github/workflows/npm-publish-beta.yml index ea7a124..230f785 100644 --- a/.github/workflows/npm-publish-beta.yml +++ b/.github/workflows/npm-publish-beta.yml @@ -13,19 +13,24 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - + - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '20.x' registry-url: 'https://registry.npmjs.org' - + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: '1.14.8' + - name: Install dependencies run: npm ci - + - name: Build run: npm run build - + - name: Run tests run: npm run test @@ -34,10 +39,10 @@ jobs: run: | # Get the current version from package.json CURRENT_VERSION=$(node -p "require('./package.json').version") - + # Generate a timestamp for uniqueness TIMESTAMP=$(date +"%Y%m%d%H%M%S") - + # For PR: use PR number in version if [[ "${{ github.event_name }}" == "pull_request" ]]; then BETA_VERSION="$CURRENT_VERSION-beta.pr.${{ github.event.pull_request.number }}.$TIMESTAMP" @@ -46,13 +51,13 @@ jobs: SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-7) BETA_VERSION="$CURRENT_VERSION-beta.$SHORT_SHA.$TIMESTAMP" fi - + # Set output for use in next step echo "version=$BETA_VERSION" >> $GITHUB_OUTPUT - + # Update package.json with new version npm version $BETA_VERSION --no-git-tag-version - + - name: Publish beta to npm run: npm publish --tag beta --access public env: @@ -72,12 +77,12 @@ jobs: } in_section == 1 { print; } ' CHANGELOG.md) - + # Store changelog content in output variable, handling multiline output echo "changelog<> $GITHUB_OUTPUT echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - + - name: Create GitHub Pre-release uses: actions/github-script@v6 with: @@ -86,7 +91,7 @@ jobs: const version = process.env.BETA_VERSION; const changelog = process.env.CHANGELOG; const eventType = process.env.GITHUB_EVENT_NAME; - + let releaseTitle, releaseTag; if (eventType === 'pull_request') { const prNumber = process.env.PR_NUMBER; @@ -97,7 +102,7 @@ jobs: releaseTitle = `Beta Release (${shortSha}): ${version}`; releaseTag = `beta-${shortSha}`; } - + // Delete existing tag if it exists try { await github.rest.git.deleteRef({ @@ -109,7 +114,7 @@ jobs: } catch (error) { console.log(`Tag ${releaseTag} doesn't exist or couldn't be deleted: ${error.message}`); } - + // Try to delete existing release if it exists try { const { data: releases } = await github.rest.repos.listReleases({ @@ -117,7 +122,7 @@ jobs: repo: context.repo.repo, per_page: 100 }); - + const existingRelease = releases.find(release => release.tag_name === releaseTag); if (existingRelease) { console.log(`Deleting existing release with ID: ${existingRelease.id}`); @@ -130,7 +135,7 @@ jobs: } catch (error) { console.log(`Error checking/deleting existing release: ${error.message}`); } - + // Create a new release try { const { data } = await github.rest.repos.createRelease({ @@ -145,7 +150,7 @@ jobs: draft: false, prerelease: true }); - + console.log(`Created pre-release: ${data.html_url}`); } catch (error) { console.log(`Error creating release: ${error.message}`); @@ -168,7 +173,7 @@ jobs: const version = process.env.BETA_VERSION; const packageName = '@space48/cloud-seed'; const installCmd = `npm install ${packageName}@${version}`; - + github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 1c378af..f0caf4c 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -10,22 +10,27 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - + - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '20.x' registry-url: 'https://registry.npmjs.org' - + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: '1.14.8' + - name: Install dependencies run: npm ci - + - name: Build run: npm run build - + - name: Run tests run: npm run test - + - name: Get package version id: package-version run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT @@ -34,16 +39,16 @@ jobs: run: npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - + - name: Extract changelog for version id: extract-changelog run: | VERSION="v${{ steps.package-version.outputs.version }}" echo "Extracting changelog for $VERSION" - + # Create a tag for the version git tag $VERSION - + # Extract changelog for the current version CHANGELOG_CONTENT=$(awk -v ver="$VERSION" ' BEGIN { in_section = 0; } @@ -53,12 +58,12 @@ jobs: } in_section == 1 { print; } ' CHANGELOG.md) - + # Store changelog content in output variable, handling multiline output echo "changelog<> $GITHUB_OUTPUT echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - + - name: Create GitHub Release uses: actions/github-script@v6 with: @@ -66,7 +71,7 @@ jobs: script: | const version = process.env.PACKAGE_VERSION; const changelog = process.env.CHANGELOG; - + const { data } = await github.rest.repos.createRelease({ owner: context.repo.owner, repo: context.repo.repo, @@ -76,12 +81,12 @@ jobs: draft: false, prerelease: false }); - + console.log(`Created release: ${data.html_url}`); env: PACKAGE_VERSION: ${{ steps.package-version.outputs.version }} CHANGELOG: ${{ steps.extract-changelog.outputs.changelog }} - + - name: Find related PRs and comment uses: actions/github-script@v6 with: @@ -90,20 +95,20 @@ jobs: const version = process.env.PACKAGE_VERSION; const packageName = '@space48/cloud-seed'; const installCmd = `npm install ${packageName}@${version}`; - + // Get the commit message to find PR number const { data: commit } = await github.rest.repos.getCommit({ owner: context.repo.owner, repo: context.repo.repo, ref: context.sha }); - + // Look for PR references in the commit message (like "Merge pull request #123") const prMatch = commit.commit.message.match(/Merge pull request #(\d+)/i); - + if (prMatch && prMatch[1]) { const prNumber = parseInt(prMatch[1], 10); - + // Comment on the PR await github.rest.issues.createComment({ issue_number: prNumber, @@ -111,7 +116,7 @@ jobs: repo: context.repo.repo, body: `🚀 New version published: **${version}**\n\nThis version has been published to npm and is now available.\n\nYou can install it with:\n\`\`\`bash\n${installCmd}\n\`\`\`` }); - + console.log(`Commented on PR #${prNumber} about the new release ${version}`); } else { console.log('No PR reference found in the commit message. Skipping comment.'); diff --git a/CHANGELOG.md b/CHANGELOG.md index e6f5eea..26b7aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.2.0](https://github.com/Space48/cloud-seed/compare/v5.1.0...v5.2.0) + +### Added + +- Google provider bindings are now exported as @space48/cloud-seed/providers/google +- Archive provider bindings are now exported as @space48/cloud-seed/providers/archive + +### Changed + +- Replaced prebuilt @cdktf/provider-google and @cdktf/provider-archive providers, which were no longer supported, with locally generated bindings +- Google provider upgraded to 7.41.0 +- Archive provider upgraded to 2.8.0 + +### Fixed + +- Fixed functions retaining their direct VPC egress configuration after the vpc configuration is removed in runtimeConfig + ## [5.1.0](https://github.com/Space48/cloud-seed/compare/v5.0.2...v5.1.0) ### Added diff --git a/README.md b/README.md index ab3a98a..bcabdc8 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ export const runtimeConfig: GcpConfig = { If your function needs access to resources on a VPC network (e.g. a back-office service over an IPSec VPN), you can configure it to route egress traffic through a VPC. All VPC settings are grouped under the `vpc` key in `runtimeConfig`. -> **Note:** VPC resources (connectors, networks, subnets) must already exist in your GCP project. Cloud Seed will configure the function to use them but will not create them. For gen1 functions, only the `connector` option is supported — `network` and `subnet` are ignored. For gen2 functions, if `network` or `subnet` is provided, direct VPC egress is used and the `connector` setting is ignored. Direct VPC egress requires `@cdktf/provider-google` to be built against Terraform Google provider >= 7.x. +> **Note:** VPC resources (connectors, networks, subnets) must already exist in your GCP project. Cloud Seed will configure the function to use them but will not create them. For gen1 functions, only the `connector` option is supported — `network` and `subnet` are ignored. For gen2 functions, if `network` or `subnet` is provided, direct VPC egress is used and the `connector` setting is ignored. Direct VPC egress uses the generated Google provider bindings included with this package. > **Note:** These options are incompatible with static IP settings. If a function is configured to use a static IP address, it won't be able to access VPN resources. @@ -384,7 +384,7 @@ This package can also be called via a JS API. ```typescript import { build, BaseConfig } from "@space48/cloud-seed"; -import GoogleProvider from "@cdktf/provider-google"; +import { provider as GoogleProvider, storageBucket } from "@space48/cloud-seed/providers/google"; import { GcsBackend, TerraformStack } from "cdktf"; import { Construct } from "constructs"; @@ -403,7 +403,7 @@ class CustomStack extends TerraformStack { /** * Example infrastructure */ - new GoogleProvider.StorageBucket(this, "CustomBucket", { + new storageBucket.StorageBucket(this, "CustomBucket", { name: `my-custom-bucket-${process.env.ENVIRONMENT}`, location: options.cloud.gcp.region.toUpperCase(), storageClass: "STANDARD", @@ -419,6 +419,16 @@ new CustomStack(app, "CustomStack", config); app.synth(); ``` + +The generated Google provider bindings are exported from +`@space48/cloud-seed/providers/google`. Consumer applications can use these bindings for +additional infrastructure without running `cdktf provider add`. Cloud Seed currently generates +them from Terraform Google provider `7.41.0`; the Terraform provider is downloaded during the +normal `terraform init` step. + +The generated Archive provider bindings are similarly exported from +`@space48/cloud-seed/providers/archive`. They are generated from Terraform Archive provider +`2.8.0`. # Cloud functions gen 2 It is recommended by google to use the [gen 2](https://cloud.google.com/functions/docs/concepts/version-comparison) functions wherever possible. diff --git a/cdktf.json b/cdktf.json index bb9c6d2..4944764 100644 --- a/cdktf.json +++ b/cdktf.json @@ -1,9 +1,13 @@ { "language": "typescript", "app": "node dist/bin/entrypoint.js build --env $ENVIRONMENT", - "terraformProviders": [], + "terraformProviders": [ + "hashicorp/google@7.41.0", + "hashicorp/archive@2.8.0" + ], "context": { "excludeStackIdFromLogicalIds": "true", "allowSepCharsInLogicalIds": "true" - } + }, + "projectId": "aa51f85a-d36c-4b9c-bc2f-8ebf3d963542" } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5827692..d8c0921 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,14 @@ { "name": "@space48/cloud-seed", - "version": "5.0.2", + "version": "5.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@space48/cloud-seed", - "version": "5.0.2", + "version": "5.1.0", "license": "MIT", "dependencies": { - "@cdktf/provider-archive": "^10.3.0", - "@cdktf/provider-google": "^14.11.0", "arg": "^5.0.2", "cdktf": "^0.20.10", "cdktf-cli": "^0.20.10", @@ -917,19 +915,6 @@ "prebuild-install": "^7.1.1" } }, - "node_modules/@cdktf/provider-archive": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@cdktf/provider-archive/-/provider-archive-10.3.0.tgz", - "integrity": "sha512-y5kAiCs+Ow0tOywsSx4OSME2a6VzCsvbkGMCXfyOCwJdoZN01yAIyLyVjtx/R25Vj20Q7EMC6zkE81dXSAh4Tw==", - "license": "MPL-2.0", - "engines": { - "node": ">= 18.12.0" - }, - "peerDependencies": { - "cdktf": "^0.20.0", - "constructs": "^10.3.0" - } - }, "node_modules/@cdktf/provider-generator": { "version": "0.20.10", "resolved": "https://registry.npmjs.org/@cdktf/provider-generator/-/provider-generator-0.20.10.tgz", @@ -1031,19 +1016,6 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, - "node_modules/@cdktf/provider-google": { - "version": "14.11.0", - "resolved": "https://registry.npmjs.org/@cdktf/provider-google/-/provider-google-14.11.0.tgz", - "integrity": "sha512-LqDyy+6+cRlf2VEU1XwvuCCqpW4qyaxlhkMDlt212oHpyThNC4S0M8BVhhpRdcpN5PZ1C4LO+0MbkZ3M6+5aCA==", - "license": "MPL-2.0", - "engines": { - "node": ">= 18.12.0" - }, - "peerDependencies": { - "cdktf": "^0.20.0", - "constructs": "^10.3.0" - } - }, "node_modules/@cdktf/provider-schema": { "version": "0.20.10", "resolved": "https://registry.npmjs.org/@cdktf/provider-schema/-/provider-schema-0.20.10.tgz", diff --git a/package.json b/package.json index 88bb3a4..d523d73 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,23 @@ { "name": "@space48/cloud-seed", - "version": "5.1.0", + "version": "5.2.0", "description": "Cloud infrastructure automation tool that uses Terraform CDK.", "main": "dist/index.js", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./providers/google": { + "types": "./dist/providers/google.d.ts", + "default": "./dist/providers/google.js" + }, + "./providers/archive": { + "types": "./dist/providers/archive.d.ts", + "default": "./dist/providers/archive.js" + }, + "./schemas/*": "./schemas/*" + }, "bin": { "cloud-seed": "dist/bin/entrypoint.js" }, @@ -29,8 +44,6 @@ "author": "Space48 ", "license": "MIT", "dependencies": { - "@cdktf/provider-archive": "^10.3.0", - "@cdktf/provider-google": "^14.11.0", "arg": "^5.0.2", "cdktf": "^0.20.10", "cdktf-cli": "^0.20.10", diff --git a/providers/archive.ts b/providers/archive.ts new file mode 100644 index 0000000..49af285 --- /dev/null +++ b/providers/archive.ts @@ -0,0 +1 @@ +export * from "../.gen/providers/archive"; diff --git a/providers/google.ts b/providers/google.ts new file mode 100644 index 0000000..ff52326 --- /dev/null +++ b/providers/google.ts @@ -0,0 +1 @@ +export * from "../.gen/providers/google"; diff --git a/stacks/gcp/GcpStack.ts b/stacks/gcp/GcpStack.ts index 85ee289..1780a20 100644 --- a/stacks/gcp/GcpStack.ts +++ b/stacks/gcp/GcpStack.ts @@ -21,8 +21,8 @@ import { storageBucketObject, vpcAccessConnector, cloudRunServiceIamMember, -} from "@cdktf/provider-google"; -import { provider as archiveProvider, dataArchiveFile } from "@cdktf/provider-archive"; +} from "../../providers/google"; +import { provider as archiveProvider, dataArchiveFile } from "../../providers/archive"; import { StackOptions, GcpFunction } from "./types"; export default class GcpStack extends TerraformStack { @@ -168,6 +168,28 @@ export default class GcpStack extends TerraformStack { maxInstanceCount: func.maxInstances, minInstanceCount: func.minInstances, environmentVariables, + ...(func.vpc?.network || func.vpc?.subnet + ? { + directVpcNetworkInterface: [ + { + ...(func.vpc.network !== undefined ? { network: func.vpc.network } : {}), + ...(func.vpc.subnet !== undefined ? { subnetwork: func.vpc.subnet } : {}), + }, + ], + directVpcEgress: + func.vpc.egressSettings === "all_traffic" + ? "VPC_EGRESS_ALL_TRAFFIC" + : "VPC_EGRESS_PRIVATE_RANGES_ONLY", + } + : func.vpc?.connector + ? { + vpcConnector: func.vpc.connector, + vpcConnectorEgressSettings: + func.vpc.egressSettings === "all_traffic" + ? "ALL_TRAFFIC" + : "PRIVATE_RANGES_ONLY", + } + : {}), }, location: this.options.gcpOptions.region, ...this.generateFunction2TriggerConfig(func, scheduledTopic), @@ -178,22 +200,6 @@ export default class GcpStack extends TerraformStack { if (func.type === "scheduledJob") { this.configureScheduledHttpFunction2(func, cloudFunc); } - if (func.vpc?.network || func.vpc?.subnet) { - cloudFunc.addOverride("service_config.direct_vpc_network_interface", { - ...(func.vpc?.network !== undefined ? { network: func.vpc.network } : {}), - ...(func.vpc?.subnet !== undefined ? { subnetwork: func.vpc.subnet } : {}), - }); - cloudFunc.addOverride( - "service_config.direct_vpc_egress", - func.vpc.egressSettings === "all_traffic" - ? "VPC_EGRESS_ALL_TRAFFIC" - : "VPC_EGRESS_PRIVATE_RANGES_ONLY", - ); - } else if (func.vpc?.connector) { - cloudFunc.serviceConfig.vpcConnector = func.vpc.connector; - cloudFunc.serviceConfig.vpcConnectorEgressSettings = - func.vpc.egressSettings === "all_traffic" ? "ALL_TRAFFIC" : "PRIVATE_RANGES_ONLY"; - } } // Create pubsub topics if they don't exist already.