AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Tinqs CI Orchestrator — Lambda dispatch + EC2 Spot runners Parameters: GiteaURL: Type: String Default: https://tinqs.com GiteaToken: Type: String NoEcho: true RunnerAMI: Type: AWS::EC2::Image::Id Description: Pre-baked AMI with Go, Node, Docker, AWS CLI, act_runner Subnet: Type: AWS::EC2::Subnet::Id Description: Public subnet for spot instances SecurityGroup: Type: AWS::EC2::SecurityGroup::Id Description: Security group for spot instances InstanceProfileArn: Type: String Description: IAM instance profile ARN for runners Globals: Function: Runtime: provided.al2023 Architectures: [x86_64] Timeout: 30 MemorySize: 128 Resources: # --- API Gateway --- WebhookApi: Type: AWS::Serverless::Api Properties: Name: tinqs-ci-webhook StageName: prod # --- DynamoDB --- RunsTable: Type: AWS::DynamoDB::Table Properties: TableName: tinqs-ci-runs BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: repo AttributeType: S - AttributeName: run_id AttributeType: S KeySchema: - AttributeName: repo KeyType: HASH - AttributeName: run_id KeyType: RANGE TimeToLiveSpecification: AttributeName: ttl Enabled: true # --- Dispatcher Lambda --- DispatchFunction: Type: AWS::Serverless::Function Properties: FunctionName: tinqs-ci-dispatch Handler: bootstrap CodeUri: ../dispatch/ Description: Receives webhook, starts Spot instances or invokes Lambda executor Timeout: 60 MemorySize: 256 Environment: Variables: GITEA_URL: !Ref GiteaURL GITEA_TOKEN: !Ref GiteaToken EXECUTOR_FUNCTION_NAME: !Ref ExecFunction RUNNER_AMI: !Ref RunnerAMI SUBNET: !Ref Subnet SECURITY_GROUP: !Ref SecurityGroup DDB_TABLE: !Ref RunsTable INSTANCE_PROFILE: !Ref InstanceProfileArn Policies: - LambdaInvokePolicy: FunctionName: !Ref ExecFunction - DynamoDBCrudPolicy: TableName: !Ref RunsTable - Version: '2012-10-17' Statement: - Effect: Allow Action: - ec2:RunInstances - ec2:TerminateInstances - ec2:DescribeInstances - ec2:CreateTags Resource: '*' - Effect: Allow Action: iam:PassRole Resource: !Ref InstanceProfileArn Events: Webhook: Type: Api Properties: RestApiId: !Ref WebhookApi Path: /webhook Method: POST # --- Executor Lambda (deploy-only jobs) --- ExecFunction: Type: AWS::Serverless::Function Properties: FunctionName: tinqs-ci-exec Handler: bootstrap CodeUri: ../exec/ Description: Executes deploy-only workflow steps directly in Lambda Timeout: 900 MemorySize: 2048 EphemeralStorage: Size: 5120 Environment: Variables: GITEA_URL: !Ref GiteaURL GITEA_TOKEN: !Ref GiteaToken Policies: - S3CrudPolicy: BucketName: tinqs-cli-releases - S3CrudPolicy: BucketName: arikigame.com - S3CrudPolicy: BucketName: docs.tinqs.com - Version: '2012-10-17' Statement: - Effect: Allow Action: - ecs:UpdateService - ecs:DescribeServices Resource: !Sub 'arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:service/tinqs-git/*' - Effect: Allow Action: - cloudfront:CreateInvalidation Resource: '*' # --- Cleanup Cron (every 5 min) --- CleanupRule: Type: AWS::Events::Rule Properties: Name: tinqs-ci-cleanup ScheduleExpression: 'rate(5 minutes)' State: ENABLED Targets: - Id: cleanup Arn: !GetAtt DispatchFunction.Arn Input: '{"body":"{\"action\":\"cleanup\"}","headers":{}}' CleanupPermission: Type: AWS::Lambda::Permission Properties: FunctionName: !Ref DispatchFunction Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: !GetAtt CleanupRule.Arn # --- Log Group --- CILogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: /tinqs/ci RetentionInDays: 14 Outputs: WebhookURL: Description: Configure as Gitea system webhook Value: !Sub 'https://${WebhookApi}.execute-api.${AWS::Region}.amazonaws.com/prod/webhook' DispatchArn: Value: !GetAtt DispatchFunction.Arn ExecArn: Value: !GetAtt ExecFunction.Arn RunsTable: Value: !Ref RunsTable