Changes
11 changed files (+434/-11)
-
-
@@ -5,4 +5,3 @@ # SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com># SPDX-License-Identifier: AGPL-3.0-only dprint 0.47.5 bun 1.1.45 terraform 1.10.3
-
-
docs/.terraform.lock.hcl (new)
-
@@ -0,0 +1,20 @@# This file is maintained automatically by "tofu init". # Manual edits may be lost in future updates. provider "registry.opentofu.org/hashicorp/aws" { version = "5.100.0" constraints = "~> 5.82" hashes = [ "h1:BrNG7eFOdRrRRbHdvrTjMJ8X8Oh/tiegURiKf7J2db8=", "zh:1a41f3ee26720fee7a9a0a361890632a1701b5dc1cf5355dc651ddbe115682ff", "zh:30457f36690c19307921885cc5e72b9dbeba369445815903acd5c39ac0e41e7a", "zh:42c22674d5f23f6309eaf3ac3a4f1f8b66b566c1efe1dcb0dd2fb30c17ce1f78", "zh:4cc271c795ff8ce6479ec2d11a8ba65a0a9ed6331def6693f4b9dccb6e662838", "zh:60932aa376bb8c87cd1971240063d9d38ba6a55502c867fdbb9f5361dc93d003", "zh:864e42784bde77b18393ebfcc0104cea9123da5f4392e8a059789e296952eefa", "zh:9750423138bb01ecaa5cec1a6691664f7783d301fb1628d3b64a231b6b564e0e", "zh:e5d30c4dec271ef9d6fe09f48237ec6cfea1036848f835b4e47f274b48bda5a7", "zh:e62bd314ae97b43d782e0841b13e68a3f8ec85cc762004f973ce5ce7b6cdbfd0", "zh:ea851a3c072528a4445ac6236ba2ce58ffc99ec466019b0bd0e4adde63a248e4", ] }
-
-
-
@@ -0,0 +1,2 @@SPDX-FileCopyrightText: 2025 Shota FUJI <pockawoooh@gmail.com> SPDX-License-Identifier: AGPL-3.0-only
-
-
docs/infrastructure.tf (new)
-
@@ -0,0 +1,165 @@# S3+Cloudfront 構成のサイトインフラ。独自ドメインを CNAME で Cloudfront の # ドメインに向ける前提。 # # 一回 `tofu apply` を実行してタイムアウトで異常終了を待ってから `tofu output` # を実行し、 `domain_cert_validations.resource_record_*` の内容を DNS に追記。 # その後再度 `tofu apply` を実行する。 # # SPDX-FileCopyrightText: 2025 Shota FUJI <pockawoooh@gmail.com> # SPDX-License-Identifier: AGPL-3.0-only variable "docs_domain" { description = "ドキュメントサイトをホストする最終的なドメイン" type = string } variable "aws_region" { description = "デフォルトのリージョン" type = string default = "us-west-2" } terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.82" } } required_version = ">= 1.2.0" } provider "aws" { region = var.aws_region default_tags { tags = { Service = "Yamori" Module = "Docs" } } } provider "aws" { alias = "us_east_1" region = "us-east-1" default_tags { tags = { Service = "Yamori" Module = "Docs" } } } resource "aws_s3_bucket" "origin" {} output "s3_bucket_name" { value = aws_s3_bucket.origin.id } data "aws_iam_policy_document" "s3_cf_read_policy" { statement { sid = "AllowCloudfrontReadonly" principals { type = "Service" identifiers = ["cloudfront.amazonaws.com"] } actions = ["s3:GetObject"] resources = ["${aws_s3_bucket.origin.arn}/*"] condition { test = "StringEquals" variable = "aws:SourceArn" values = [aws_cloudfront_distribution.cdn.arn] } } } resource "aws_s3_bucket_policy" "allow_read_from_cloudfront" { bucket = aws_s3_bucket.origin.id policy = data.aws_iam_policy_document.s3_cf_read_policy.json } resource "aws_acm_certificate" "domain_cert" { # CloudFront で使う ACM は us-east-1 にある必要がある。 # https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cnames-and-https-requirements.html provider = aws.us_east_1 domain_name = var.docs_domain validation_method = "DNS" lifecycle { create_before_destroy = true } } output "domain_cert_validations" { value = aws_acm_certificate.domain_cert.domain_validation_options } locals { cf_origin_id = "yamor_docs_cdn" } resource "aws_cloudfront_origin_access_control" "s3_oac" { name = "static_website" origin_access_control_origin_type = "s3" signing_behavior = "always" signing_protocol = "sigv4" } resource "aws_cloudfront_distribution" "cdn" { origin { domain_name = aws_s3_bucket.origin.bucket_regional_domain_name origin_id = local.cf_origin_id origin_access_control_id = aws_cloudfront_origin_access_control.s3_oac.id } enabled = true is_ipv6_enabled = true default_root_object = "index.html" http_version = "http2and3" aliases = [var.docs_domain] viewer_certificate { acm_certificate_arn = aws_acm_certificate.domain_cert.arn ssl_support_method = "sni-only" } default_cache_behavior { # AWS が管理している CacheOptimized ポリシー cache_policy_id = "658327ea-f89d-4fab-a63d-7e88639e58f6" allowed_methods = ["GET", "HEAD", "OPTIONS"] cached_methods = ["GET", "HEAD"] target_origin_id = local.cf_origin_id compress = true viewer_protocol_policy = "redirect-to-https" min_ttl = 0 default_ttl = 31536000 max_ttl = 31536000 } restrictions { geo_restriction { locations = [] restriction_type = "none" } } } output "cloudfront_distribution_id" { value = aws_cloudfront_distribution.cdn.id } output "cloudfront_domain" { value = aws_cloudfront_distribution.cdn.domain_name }
-
-
-
@@ -15,6 +15,8 @@site_name: "Yamori ユーザガイド" docs_dir: "src" use_directory_urls: false theme: name: material palette:
-
-
-
@@ -31,7 +31,11 @@ "exec": {"commands": [ { "exts": ["tf"], "command": "terraform fmt -" "command": "tofu fmt -" }, { "exts": ["go"], "command": "gofmt" }, { "exts": ["nix"],
-
-
-
@@ -28,8 +28,10 @@ flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; deploy-docs = pkgs.callPackage ./tools/deploy-docs { }; in { rec { packages = { docs = pkgs.callPackage ./docs/package.nix { }; };
-
@@ -67,18 +69,60 @@ {type = "app"; program = "${server}/bin/mkdocs"; }; # nix run .#deploy-docs # --------------------- # ドキュメントをビルドしてデプロイをする。 # リポジトリルートから実行する必要がある。 # "docs/" で Tofu を実行して環境をセットアップしていないと失敗する。 deploy-docs = let cmd = pkgs.symlinkJoin { name = "deploy-docs-wrapped"; nativeBuildInputs = [ pkgs.makeWrapper packages.docs ]; paths = [ deploy-docs ]; postBuild = '' wrapProgram $out/bin/deploydocs \ --add-flags '-dir' \ --add-flags ${packages.docs} \ --add-flags '-tf-dir' \ --add-flags 'docs' ''; }; in { type = "app"; program = "${cmd}/bin/deploydocs"; }; }; devShell = pkgs.mkShell { packages = with pkgs; [ # Official formatter for Nix code # https://hackage.haskell.org/package/nixfmt nixfmt-rfc-style packages = with pkgs; [ # Official formatter for Nix code # https://hackage.haskell.org/package/nixfmt nixfmt-rfc-style # Copyright and license linter based on SPDX # https://github.com/fsfe/reuse-tool reuse # Go Programming language # https://go.dev/ go # Copyright and license linter based on SPDX # https://github.com/fsfe/reuse-tool reuse ]; # Official language server for the Go language # https://github.com/golang/tools/tree/master/gopls gopls ] ++ deploy-docs.buildInputs; }; } );
-
-
-
@@ -0,0 +1,49 @@# SPDX-License-Identifier: AGPL-3.0-only # Copyright 2025 Shota FUJI # # This program is free software: you can redistribute it and/or modify it under the terms # of the GNU Affero General Public License as published by the Free Software Foundation, # either version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. { buildGoModule, lib, awscli2, opentofu, }: buildGoModule { name = "deploy-docs"; src = lib.sourceFilesBySuffices ./. [ ".go" ".mod" ]; buildInputs = [ # Unified tool to manage your AWS services # https://aws.amazon.com/cli/ awscli2 # Tool for building, changing, and versioning infrastructure # https://opentofu.org/ opentofu ]; vendorHash = null; ldflags = [ "-X main.awsCliBin=${awscli2}/bin/aws" "-X pocka.jp/yamori/deploydocs/tofu.tofuBin=${opentofu}/bin/tofu" ]; meta = { mainProgram = "deploydocs"; }; }
-
-
tools/deploy-docs/go.mod (new)
-
@@ -0,0 +1,6 @@// SPDX-FileCopyrightText: 2025 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only module pocka.jp/yamori/deploydocs go 1.24.1
-
-
-
@@ -0,0 +1,65 @@// tools/deploy-docs/cmd.go -- ドキュメントサイトのデプロイCLI // // SPDX-FileCopyrightText: 2025 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only package main import ( "flag" "fmt" "log" "os" "os/exec" "pocka.jp/yamori/deploydocs/tofu" ) var awsCliBin = "aws" func main() { tfDir := flag.String("tf-dir", "", "Tofu を実行したディレクトリ") dir := flag.String("dir", "", "デプロイするディレクトリ") flag.Parse() if dir == nil || *dir == "" { log.Fatal("dir は必須です") } if tfDir == nil || *tfDir == "" { log.Fatal("tf-dir は必須です") } output, err := tofu.NewOutput(*tfDir) if err != nil { log.Fatal(err) } s3Sync := exec.Command( awsCliBin, "s3", "sync", "--delete", *dir, fmt.Sprintf("s3://%s", output.S3BucketName), ) s3Sync.Stdin = os.Stdin s3Sync.Stdout = os.Stdout s3Sync.Stderr = os.Stderr if err := s3Sync.Run(); err != nil { log.Fatal(err) } cfInvalidation := exec.Command( awsCliBin, "cloudfront", "create-invalidation", "--distribution-id", output.CloudfrontDistributionID, "--paths", "/*", ) cfInvalidation.Stdin = os.Stdin cfInvalidation.Stdout = os.Stdout cfInvalidation.Stderr = os.Stderr if err := cfInvalidation.Run(); err != nil { log.Fatal(err) } }
-
-
-
@@ -0,0 +1,67 @@// SPDX-FileCopyrightText: 2025 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only package tofu import ( "encoding/json" "fmt" "log" "os/exec" ) var tofuBin = "tofu" type stringOutput struct { Value string } type rawOutput struct { CloudfrontDistributionID stringOutput `json:"cloudfront_distribution_id"` S3BucketName stringOutput `json:"s3_bucket_name"` } type Output struct { CloudfrontDistributionID string S3BucketName string } func NewOutput(tfDir string) (*Output, error) { cmd := exec.Command(tofuBin, fmt.Sprintf("-chdir=%s", tfDir), "output", "-json") stdout, err := cmd.StdoutPipe() if err != nil { return nil, err } if err := cmd.Start(); err != nil { return nil, err } var raw rawOutput if err := json.NewDecoder(stdout).Decode(&raw); err != nil { return nil, fmt.Errorf("Tofu の出力を読み込めませんでした: %s", err) } if err := cmd.Wait(); err != nil { return nil, err } output := Output{ CloudfrontDistributionID: raw.CloudfrontDistributionID.Value, S3BucketName: raw.S3BucketName.Value, } if output.CloudfrontDistributionID == "" { return nil, fmt.Errorf("cloudfront_distribution_id が取得できませんでした") } if output.S3BucketName == "" { return nil, fmt.Errorf("s3_bucket_name が取得できませんでした") } log.Printf("cloudfront_distribution_id: %s\n", output.CloudfrontDistributionID) log.Printf("s3_bucket_name: %s\n", output.S3BucketName) return &output, nil }
-