diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 26ded7b..eeb1eac 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,23 +9,35 @@ jobs: update: name: Update runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + pull-requests: write steps: - uses: actions/checkout@v6 - name: Setup Node uses: actions/setup-node@v6 with: node-version: 24.x + - name: Setup gcloud + uses: google-github-actions/setup-gcloud@v2 + with: + install_components: gke-gcloud-auth-plugin - name: Install Packages working-directory: ./pulumi run: npm install - name: TypeScript Check working-directory: ./pulumi run: npm run check + - name: Authenticate Pulumi + uses: pulumi/auth-actions@v1 + with: + organization: meiermade + requested-token-type: urn:pulumi:token-type:access_token:personal + scope: user:meiermade - name: Update uses: pulumi/actions@v6 with: work-dir: ./pulumi command: up stack-name: prod - env: - PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }} diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 9810459..9bda1b9 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -29,6 +29,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + id-token: write pull-requests: write steps: - uses: actions/checkout@v6 @@ -36,12 +37,22 @@ jobs: uses: actions/setup-node@v6 with: node-version: 24.x + - name: Setup gcloud + uses: google-github-actions/setup-gcloud@v2 + with: + install_components: gke-gcloud-auth-plugin - name: Install Packages working-directory: ./pulumi run: npm install - name: TypeScript Check working-directory: ./pulumi run: npm run check + - name: Authenticate Pulumi + uses: pulumi/auth-actions@v1 + with: + organization: meiermade + requested-token-type: urn:pulumi:token-type:access_token:personal + scope: user:meiermade - name: Preview uses: pulumi/actions@v6 with: @@ -50,5 +61,3 @@ jobs: stack-name: prod diff: true comment-on-pr: true - env: - PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }} diff --git a/app/src/App/src/Config.fs b/app/src/App/src/Config.fs index 3734c3d..feba4a5 100644 --- a/app/src/App/src/Config.fs +++ b/app/src/App/src/Config.fs @@ -4,13 +4,11 @@ open Domain open System type SeqConfig = - { endpoint:string - apiKey:string } + { endpoint:string } module SeqConfig = let load () = - { endpoint = Env.variableOrDefault "SEQ_ENDPOINT" "http://localhost:5341" - apiKey = Env.variableOrDefault "SEQ_API_KEY" "" } + { endpoint = Env.variableOrDefault "SEQ_ENDPOINT" "http://localhost:5341" } type ServerConfig = { url:string } diff --git a/app/src/App/src/Program.fs b/app/src/App/src/Program.fs index 47c80ea..a9f124e 100644 --- a/app/src/App/src/Program.fs +++ b/app/src/App/src/Program.fs @@ -24,8 +24,7 @@ let configureTracerProvider (config: Config) = .AddHttpClientInstrumentation() .AddOtlpExporter(fun opts -> opts.Endpoint <- Uri(config.seq.endpoint + "/ingest/otlp/v1/traces") - opts.Protocol <- OtlpExportProtocol.HttpProtobuf - opts.Headers <- $"X-Seq-ApiKey={config.seq.apiKey}") + opts.Protocol <- OtlpExportProtocol.HttpProtobuf) .Build() let configureLogger (config: Config) = @@ -39,7 +38,7 @@ let configureLogger (config: Config) = LoggerConfiguration() .MinimumLevel.ControlledBy(levelSwitch) .WriteTo.Console() - .WriteTo.Seq(serverUrl = config.seq.endpoint, apiKey = config.seq.apiKey, controlLevelSwitch = levelSwitch) + .WriteTo.Seq(serverUrl = config.seq.endpoint, controlLevelSwitch = levelSwitch) .CreateLogger() Log.Logger <- logger diff --git a/app/src/Domain/Notion.fs b/app/src/Domain/Notion.fs index 2c6cb6b..5c9d6ea 100644 --- a/app/src/Domain/Notion.fs +++ b/app/src/Domain/Notion.fs @@ -46,12 +46,12 @@ module Cursor = type Config = { articlesDatabaseId:DatabaseId - token:string } + apiKey:string } module Config = let load () = { articlesDatabaseId = Env.variable "NOTION_ARTICLES_DATABASE_ID" |> DatabaseId.ofString - token = Env.variable "NOTION_TOKEN" } + apiKey = Env.variable "NOTION_API_KEY" } // ============================================================ // Types @@ -359,7 +359,7 @@ let private BaseUrl = "https://api.notion.com/v1" let private createRequest (config: Config) (method: HttpMethod) (url: string) = let req = new HttpRequestMessage(method, url) - req.Headers.Authorization <- AuthenticationHeaderValue("Bearer", config.token) + req.Headers.Authorization <- AuthenticationHeaderValue("Bearer", config.apiKey) req.Headers.Add("Notion-Version", NotionApiVersion) req diff --git a/app/src/Tests/ArticleServiceTests.fs b/app/src/Tests/ArticleServiceTests.fs index 650ca4b..dd8c9b2 100644 --- a/app/src/Tests/ArticleServiceTests.fs +++ b/app/src/Tests/ArticleServiceTests.fs @@ -30,7 +30,7 @@ let private telemetry () = let private notionConfig: Notion.Config = { articlesDatabaseId = Notion.DatabaseId.ofString "db" - token = "token" } + apiKey = "token" } let private emptyNotionService: Notion.Service = { queryDatabase = diff --git a/app/src/Tests/NotionServiceTests.fs b/app/src/Tests/NotionServiceTests.fs index 4c78f3c..2025657 100644 --- a/app/src/Tests/NotionServiceTests.fs +++ b/app/src/Tests/NotionServiceTests.fs @@ -55,7 +55,7 @@ let private mockHttpClient (responses: (string * string) list) = let private testConfig: Config = { articlesDatabaseId = DatabaseId.ofString "articles-db" - token = "test-token" } + apiKey = "test-token" } let private noopTelemetry: Telemetry.Service = { startActiveSpan = diff --git a/pulumi/index.ts b/pulumi/index.ts index 22e4135..ad6bdc9 100644 --- a/pulumi/index.ts +++ b/pulumi/index.ts @@ -1,4 +1,3 @@ -import './src/aws' import './src/cloudflare' import './src/docker' import './src/k8s' diff --git a/pulumi/package-lock.json b/pulumi/package-lock.json index acb9dc8..821ad99 100644 --- a/pulumi/package-lock.json +++ b/pulumi/package-lock.json @@ -6,9 +6,9 @@ "": { "name": "andymeier.dev", "dependencies": { - "@pulumi/aws": "^7.20.0", "@pulumi/cloudflare": "^6.13.0", - "@pulumi/docker": "^4.11.0", + "@pulumi/docker-build": "^0.0.15", + "@pulumi/gcp": "^9.16.0", "@pulumi/kubernetes": "^4.26.0", "@pulumi/pulumi": "^3.224.0", "@pulumi/random": "^4.19.1" @@ -481,7 +481,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -778,16 +777,6 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", "license": "BSD-3-Clause" }, - "node_modules/@pulumi/aws": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@pulumi/aws/-/aws-7.20.0.tgz", - "integrity": "sha512-f8efmjHXKRUQY4fojfw7vuWN7Q07Cz0bjqbIwD/qtOuz+qs+zrKWU50uAB0HcWXAxjI0ll4KslGu2oLRiMTxng==", - "license": "Apache-2.0", - "dependencies": { - "@pulumi/pulumi": "^3.142.0", - "mime": "^2.0.0" - } - }, "node_modules/@pulumi/cloudflare": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/@pulumi/cloudflare/-/cloudflare-6.13.0.tgz", @@ -797,14 +786,215 @@ "@pulumi/pulumi": "^3.142.0" } }, - "node_modules/@pulumi/docker": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@pulumi/docker/-/docker-4.11.0.tgz", - "integrity": "sha512-NUXMCWL3VBtlclC4u7G9LcToCQGiR/wOuRdKHzAqbn0nzaVuPV3NlDZIz7CpcHL/mGGIQ00/HnLGCA8lC1kSFA==", + "node_modules/@pulumi/docker-build": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@pulumi/docker-build/-/docker-build-0.0.15.tgz", + "integrity": "sha512-abtz4zCbePBkpj73M7mJWqXktM9muEAUOxRu8PKErbsqv5M3NfALCMULsDxA1a9vEImHa6ZC9rAe1liXbsinFg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@pulumi/pulumi": "^3.142.0" + } + }, + "node_modules/@pulumi/gcp": { + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/@pulumi/gcp/-/gcp-9.16.0.tgz", + "integrity": "sha512-ox7CJIfFjPVpSRiLBt1yxc04+xvqUzRKiJbpkbEcc2fHelBNyDx4sFuMH2OlLjjRa1OPbuGODAekLBKGt3spEQ==", "license": "Apache-2.0", "dependencies": { + "@npmcli/package-json": "^6.2.0", "@pulumi/pulumi": "^3.142.0", - "semver": "^5.4.0" + "@types/express": "^4.16.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/@npmcli/git": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-6.0.3.tgz", + "integrity": "sha512-GUYESQlxZRAdhs3UhbB6pVRNUELQOHXwK9ruDkwmCv2aZ5y0SApQzUJCg02p3A7Ue2J5hxvlk1YI53c00NmRyQ==", + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/@npmcli/package-json": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-6.2.0.tgz", + "integrity": "sha512-rCNLSB/JzNvot0SEyXqWZ7tX2B5dD2a1br2Dp0vSYVo5jh8Z0EZ7lS9TsZ1UtziddB1UfNUaMCc538/HztnJGA==", + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/@npmcli/promise-spawn": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.3.tgz", + "integrity": "sha512-Yb00SWaL4F8w+K8YGhQ55+xE4RUNdMHV43WZGsiTM92gS+lC0mGsn7I4hLug7pbao035S6bj3Y3w0cUNGLfmkg==", + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@pulumi/gcp/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/@pulumi/gcp/node_modules/npm-install-checks": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.2.tgz", + "integrity": "sha512-z9HJBCYw9Zr8BqXcllKIs5nI+QggAImbBdHphOzVYrz2CB4iQ6FzWyKmlqDZua+51nAu7FcemlbTc9VgQN5XDQ==", + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/npm-package-arg": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", + "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", + "integrity": "sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==", + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pulumi/gcp/node_modules/validate-npm-package-name": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", + "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@pulumi/gcp/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@pulumi/kubernetes": { @@ -1011,6 +1201,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -1023,6 +1223,39 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, "node_modules/@types/google-protobuf": { "version": "3.15.12", "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.15.12.tgz", @@ -1035,6 +1268,12 @@ "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", "license": "MIT" }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -1044,6 +1283,12 @@ "@types/node": "*" } }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.10.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.15.tgz", @@ -1053,6 +1298,18 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", @@ -1068,6 +1325,36 @@ "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", "license": "MIT" }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "node_modules/@types/shimmer": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", @@ -1094,7 +1381,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2148,18 +2434,6 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "license": "MIT" }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -2748,7 +3022,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -2968,15 +3241,6 @@ "license": "MIT", "optional": true }, - "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3355,7 +3619,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/pulumi/package.json b/pulumi/package.json index 082465f..8a5ff1d 100644 --- a/pulumi/package.json +++ b/pulumi/package.json @@ -9,9 +9,9 @@ "typescript": "^5.9.3" }, "dependencies": { - "@pulumi/aws": "^7.20.0", "@pulumi/cloudflare": "^6.13.0", - "@pulumi/docker": "^4.11.0", + "@pulumi/docker-build": "^0.0.15", + "@pulumi/gcp": "^9.16.0", "@pulumi/kubernetes": "^4.26.0", "@pulumi/pulumi": "^3.224.0", "@pulumi/random": "^4.19.1" diff --git a/pulumi/src/aws/index.ts b/pulumi/src/aws/index.ts deleted file mode 100644 index 982aaed..0000000 --- a/pulumi/src/aws/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -import './provider' -import './repository' diff --git a/pulumi/src/aws/provider.ts b/pulumi/src/aws/provider.ts deleted file mode 100644 index 3bb5d1e..0000000 --- a/pulumi/src/aws/provider.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as aws from '@pulumi/aws' -import * as config from '../config' - -export const provider = new aws.Provider('default', { - region: config.awsConfig.region, - allowedAccountIds: [ config.awsConfig.platformAccountId ] -}) diff --git a/pulumi/src/aws/repository.ts b/pulumi/src/aws/repository.ts deleted file mode 100644 index c40f473..0000000 --- a/pulumi/src/aws/repository.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as aws from '@pulumi/aws' -import { provider } from './provider' -import * as config from '../config' - -export const repo = new aws.ecr.Repository(config.identifier, { - name: config.identifier -}, { provider }) - -export const credentials = aws.ecr.getAuthorizationTokenOutput({ - registryId: repo.registryId -}, { provider }) - -new aws.ecr.RepositoryPolicy(config.identifier, { - repository: repo.name, - policy: { - Version: '2012-10-17', - Statement: [ - { - Effect: 'Allow', - Principal: { - AWS: config.awsConfig.eksNodeManagerArn - }, - Action: [ - 'ecr:GetDownloadUrlForLayer', - 'ecr:BatchGetImage', - 'ecr:BatchCheckLayerAvailability' - ] - } - ] - } -}, { provider: provider }) - -new aws.ecr.LifecyclePolicy(config.identifier, { - repository: repo.name, - policy: { - rules: [{ - rulePriority: 1, - selection: { - tagStatus: 'any', - countType: 'imageCountMoreThan', - countNumber: 1 - }, - action: { - type: 'expire' - }, - }] - } -}, { provider }) diff --git a/pulumi/src/config.ts b/pulumi/src/config.ts index a98ee11..fcb03fb 100644 --- a/pulumi/src/config.ts +++ b/pulumi/src/config.ts @@ -5,34 +5,36 @@ export const rootDir = path.dirname(path.dirname(__dirname)) export const identifier = 'andymeier' -const rawAwsConfig = new pulumi.Config('aws') -const rawCloudflareConfig = new pulumi.Config('cloudflare') -const rawK8sConfig = new pulumi.Config('k8s') -const rawSeqConfig = new pulumi.Config('seq') -const rawNotionConfig = new pulumi.Config('notion') +export const rawDockerConfig = new pulumi.Config('docker') -export const awsConfig = { - platformAccountId: rawAwsConfig.require('platformAccountId'), - region: rawAwsConfig.require('region'), - eksNodeManagerArn: rawAwsConfig.require('eksNodeManagerArn') +export const dockerConfig = { + registryUri: rawDockerConfig.require('registryUri'), + registryAccessToken: rawDockerConfig.requireSecret('registryAccessToken'), } +const rawCloudflareConfig = new pulumi.Config('cloudflare') + export const cloudflareConfig = { accountId: rawCloudflareConfig.require('accountId'), apiToken: rawCloudflareConfig.requireSecret('apiToken'), cloudflaredVersion: '2026.2.0' } +const rawK8sConfig = new pulumi.Config('k8s') + export const k8sConfig = { namespace: rawK8sConfig.require('namespace') } +const rawSeqConfig = new pulumi.Config('seq') + export const seqConfig = { - endpoint: rawSeqConfig.require('endpoint'), - apiKey: rawSeqConfig.requireSecret('apiKey') + endpoint: rawSeqConfig.require('endpoint') } +const rawNotionConfig = new pulumi.Config('notion') + export const notionConfig = { articlesDatabaseId: rawNotionConfig.require('articlesDatabaseId'), - token: rawNotionConfig.requireSecret('token') + apiKey: rawNotionConfig.requireSecret('apiKey') } diff --git a/pulumi/src/docker/image.ts b/pulumi/src/docker/image.ts index 79e95f0..9bbdba2 100644 --- a/pulumi/src/docker/image.ts +++ b/pulumi/src/docker/image.ts @@ -1,28 +1,28 @@ import * as pulumi from '@pulumi/pulumi' -import * as docker from '@pulumi/docker' +import * as dockerBuild from '@pulumi/docker-build' import * as path from 'path' -import { provider } from './provider' -import * as repository from '../aws/repository' import * as config from '../config' +import { provider } from './provider' -const registry:pulumi.Output = - repository.credentials.apply(creds => { - let decoded = Buffer.from(creds.authorizationToken, 'base64').toString('utf-8') - let [username, password] = decoded.split(':') - return { - server: creds.proxyEndpoint, - username: username, - password: password - } - }) +const registryUri = config.dockerConfig.registryUri +const registryHost = registryUri.split('/')[0] -export const image = new docker.Image(config.identifier, { - imageName: repository.repo.repositoryUrl, - build: { - context: path.join(config.rootDir, 'app'), - platform: 'linux/arm64' +export const image = new dockerBuild.Image(config.identifier, { + tags: [ + pulumi.interpolate`${registryUri}/${config.identifier}` + ], + context: { + location: path.join(config.rootDir, 'app') }, - registry: registry + platforms: [ + dockerBuild.Platform.Linux_amd64 + ], + push: true, + registries: [{ + address: registryHost, + username: 'oauth2accesstoken', + password: config.dockerConfig.registryAccessToken, + }], }, { provider }) -export const imageName = image.repoDigest.apply(digest => digest!) +export const imageRef = image.ref diff --git a/pulumi/src/docker/provider.ts b/pulumi/src/docker/provider.ts index d67a6a2..c949240 100644 --- a/pulumi/src/docker/provider.ts +++ b/pulumi/src/docker/provider.ts @@ -1,3 +1,3 @@ -import * as docker from '@pulumi/docker' +import * as dockerBuild from '@pulumi/docker-build' -export const provider = new docker.Provider('default') +export const provider = new dockerBuild.Provider('default') diff --git a/pulumi/src/k8s/deployment.ts b/pulumi/src/k8s/deployment.ts index 3a87260..e78ad7d 100644 --- a/pulumi/src/k8s/deployment.ts +++ b/pulumi/src/k8s/deployment.ts @@ -14,10 +14,9 @@ let appSecret = new k8s.core.v1.Secret('app', { ASPNETCORE_ENVIRONMENT: 'Production', SERVER_URL: 'http://0.0.0.0:5000', SEQ_ENDPOINT: config.seqConfig.endpoint, - SEQ_API_KEY: config.seqConfig.apiKey, SQLITE_PATH: '/data/app.db', NOTION_ARTICLES_DATABASE_ID: config.notionConfig.articlesDatabaseId, - NOTION_TOKEN: config.notionConfig.token, + NOTION_API_KEY: config.notionConfig.apiKey, } }, { provider }) @@ -63,7 +62,7 @@ const deployment = new k8s.apps.v1.Deployment('app', { containers: [ { name: 'app', - image: image.imageName, + image: image.imageRef, securityContext: containerSecurityContext, imagePullPolicy: 'IfNotPresent', envFrom: [{ secretRef: { name: appSecret.metadata.name } }],