Skip to content

Commit f452ac4

Browse files
committed
[docs] readme update
1 parent a52b38e commit f452ac4

9 files changed

Lines changed: 309 additions & 145 deletions

File tree

CONCEPT.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Concept: Taskfile-Centric Development
2+
## Why
3+
Teams waste time fighting environment drift between local dev and CI.
4+
Scripts scatter across repos, CI has hidden steps, and debugging becomes trial-and-error.
5+
We fix this by making **Taskfile the single source of truth** and running everything in **secure containers** — locally and in CI.
6+
7+
## Who This Is For
8+
- Teams with complex build/test pipelines
9+
- Infrastructure-heavy projects (AWS, Terraform, CD)
10+
- Organizations wanting reproducibility and quick onboarding
11+
12+
## Core Principles
13+
1. **Reproducibility** — same Task runs identically everywhere
14+
2. **Isolation** — no host dependencies beyond Docker + Task
15+
3. **Security by default** — non-root, dropped caps, no-new-privileges
16+
4. **Transparency** — no CI-only magic; everything in `Taskfile.yml`
17+
5. **Separation of concerns** — CI (dev tasks) in Taskfile; CD (deploy) in Actions
18+
19+
## Container Security Defaults
20+
- `--cap-drop=ALL` — no privileged capabilities
21+
- `--security-opt no-new-privileges` — prevent privilege escalation
22+
- `--user $(id -u):$(id -g)` — non-root execution
23+
- `--workdir /workspace` — consistent working directory
24+
- Project mount only: `/workspace` (read-write)
25+
26+
## Container Runtime Pattern
27+
Pull image once (quiet):
28+
```yaml
29+
_docker/pull:
30+
internal: true
31+
cmds:
32+
- |
33+
if ! docker image inspect "{{.IMAGE}}" >/dev/null 2>&1; then
34+
docker pull -q "{{.IMAGE}}" >/dev/null 2>&1 || {
35+
echo "Failed to pull image: {{.IMAGE}}"
36+
exit 1
37+
}
38+
fi
39+
silent: true
40+
requires:
41+
vars: [IMAGE]
42+
```
43+
Run securely (never pull during execution):
44+
```yaml
45+
_docker/run:
46+
internal: true
47+
dir: "{{.git_root}}"
48+
deps:
49+
- task: _docker/pull
50+
vars: { IMAGE: "{{.IMAGE}}" }
51+
cmd: |
52+
docker run --rm --init --pull=never {{if .TTY}}-it{{end}} \
53+
--cap-drop=ALL \
54+
--security-opt no-new-privileges \
55+
--user $(id -u):$(id -g) \
56+
--workdir /workspace \
57+
{{if .ENVS}}{{range $e := .ENVS}}--env {{$e}} {{end}}{{end}}\
58+
{{if .PORTS}}{{range $p := .PORTS}}--publish {{$p}} {{end}}{{end}}\
59+
{{if .VOLUMES}}{{range $v := .VOLUMES}}--volume {{$v}} {{end}}{{end}}\
60+
--volume "{{.git_root}}/{{.MOUNT_DIR}}:/workspace:rw" \
61+
"{{.IMAGE}}" \
62+
{{.CMD}}
63+
silent: true
64+
requires:
65+
vars: [IMAGE, CMD, MOUNT_DIR]
66+
```
67+
68+
### Runtime Variables
69+
| Variable | Purpose | Example |
70+
|---|---|---|
71+
|`IMAGE`|Docker image (pin in CI)|`node:20`, `golang:1.22@sha256:...`|
72+
|`CMD`|Command to execute|`sh -c 'npm ci && npm test'`|
73+
|`MOUNT_DIR`|Project directory to mount|`"."`, `"site"`, `"infra"`|
74+
|`ENVS`|Environment variables|`["GOOS=linux","NPM_CONFIG_CACHE=/cache"]`|
75+
|`PORTS`|Port mappings for dev servers|`["3000:3000"]`|
76+
|`VOLUMES`|Additional mounts|`["$HOME/.ssh:/ssh:ro"]`|
77+
|`TTY`|Interactive mode|`"true"` for dev servers|
78+
79+
## Task Examples
80+
```yaml
81+
lint:
82+
desc: "Run code linting"
83+
cmds:
84+
- task: _docker/run
85+
vars:
86+
IMAGE: "node:20"
87+
MOUNT_DIR: "."
88+
ENVS: ["NPM_CONFIG_CACHE=/workspace/.cache"]
89+
CMD: "sh -c 'npm ci && npx eslint .'"
90+
91+
test:
92+
desc: "Run test suite"
93+
cmds:
94+
- task: _docker/run
95+
vars:
96+
IMAGE: "golang:1.22"
97+
MOUNT_DIR: "."
98+
CMD: "go test ./..."
99+
100+
dev:
101+
desc: "Development server with hot reload"
102+
cmds:
103+
- task: _docker/run
104+
vars:
105+
IMAGE: "node:20"
106+
MOUNT_DIR: "."
107+
PORTS: ["3000:3000"]
108+
TTY: "true"
109+
CMD: "sh -c 'npm ci && npm run dev -- --host 0.0.0.0'"
110+
```
111+
112+
## CI Integration
113+
Use the same tasks in GitHub Actions:
114+
```yaml
115+
jobs:
116+
ci:
117+
runs-on: ubuntu-latest
118+
steps:
119+
- uses: actions/checkout@v4
120+
121+
- uses: Mad-Pixels/github-workflows/actions/taskfile-runner@v1
122+
with:
123+
command: "lint"
124+
125+
- uses: Mad-Pixels/github-workflows/actions/taskfile-runner@v1
126+
with:
127+
command: "test"
128+
```
129+
130+
## What Goes Where
131+
|✅ Include in Taskfile|❌ Keep in Actions|
132+
|---|---|
133+
|Code linting/formatting|AWS deployments|
134+
|Unit/integration tests|Infrastructure provisioning|
135+
|Building/compilation|Production secrets handling|
136+
|Development servers|Cloud resource management|
137+
|Static analysis|Terraform apply operations|
138+
139+
## Benefits
140+
- 💰 **Reduced maintenance** — no more "works on my machine"
141+
- 🚀 **Faster delivery** — fewer environment-specific bugs
142+
- 🔐 **Stronger security** — containerized, non-root execution
143+
- 📋 **Clear audit trails** — Git history = deployment history
144+
- 🧠 **Better onboarding** — new engineers only need Docker and Task
145+
146+
## Local-to-CI Guarantee
147+
If it works locally, it works in CI — **guaranteed**:
148+
- Same Docker images and commands
149+
- Same environment variables and mounts
150+
- No CI-specific scripts or hidden steps
151+
- Debug locally, not through failed pipelines
152+
153+
## Best Practices
154+
- **Pin images by digest in CI** for determinism: `node:20@sha256:...`
155+
- **Use project-local caches** under `/workspace/.cache`
156+
- **Mount read-only where possible**: `["$HOME/.ssh:/ssh:ro"]`
157+
- **Validate inputs** in task descriptions and error messages
158+
- **Keep secrets out of Taskfile** — handle via Actions only
159+
160+
## Real Project Examples
161+
| Name | Description |
162+
|---|---|
163+
|**[about](https://github.com/mr-chelyshkin/about)**|VueJS static site with containerized build and AWS deployment|
164+

actions/aws-cloudfront-invalidation/readme.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,24 @@ jobs:
4040
```
4141
4242
## 📥 Inputs
43-
| **Name** | **Required** | **Description** | **Default** |
44-
|--------------------|--------------|---------------------------------------------------------------------------------------------------------|-------------|
45-
| `aws_region` | ✅ Yes | AWS region (used by the CLI) | - |
46-
| `distribution_id` | ✅ Yes | CloudFront distribution ID (format: E + 13 alphanumeric chars, e.g. `E1234567890ABC`) | - |
47-
| `aws_access_key` | ❌ No | AWS access key ID (optional if using OIDC) | - |
48-
| `aws_secret_key` | ❌ No | AWS secret access key (optional if using OIDC) | - |
49-
| `role_to_assume` | ❌ No | AWS IAM role ARN to assume (OIDC) | - |
50-
| `paths` | ❌ No | Space‑separated list of paths to invalidate (must start with `/`; max 1000 entries; wildcards allowed) | `/*` |
51-
| `caller_reference` | ❌ No | Custom caller reference for idempotency (auto‑generated if not provided) | - |
52-
| `show_summary` | ❌ No | Print summary with task output in job summary | `true` |
53-
| `summary_limit` | ❌ No | Max number of output lines to show in summary | `250` |
43+
|**Name**|**Required**|**Description**|**Default**|
44+
|---|---|---|---|
45+
|`aws_region`|✅ Yes|AWS region (used by the CLI)|-|
46+
|`distribution_id`|✅ Yes|CloudFront distribution ID (format: E + 13 alphanumeric chars, e.g. `E1234567890ABC`)|-|
47+
|`aws_access_key`|❌ No|AWS access key ID (optional if using OIDC)|-|
48+
|`aws_secret_key`|❌ No|AWS secret access key (optional if using OIDC)|-|
49+
|`role_to_assume`|❌ No|AWS IAM role ARN to assume (OIDC)|-|
50+
|`paths`|❌ No|Space‑separated list of paths to invalidate (must start with `/`; max 1000 entries; wildcards allowed)|`/*`|
51+
|`caller_reference`|❌ No|Custom caller reference for idempotency (auto‑generated if not provided)|-|
52+
|`show_summary`|❌ No|Print summary with task output in job summary|`true`|
53+
|`summary_limit`|❌ No|Max number of output lines to show in summary|`250`|
5454

5555
## 📤 Outputs
56-
| **Name** | **Description** |
57-
|-------------------|-------------------------------------|
58-
| `invalidation_id` | ID of the created invalidation |
59-
| `status` | Status returned by CloudFront |
60-
| `caller_reference`| Reference used for this invalidation|
56+
| **Name**|**Description**|
57+
|---|---|
58+
|`invalidation_id`|ID of the created invalidation|
59+
|`status`|Status returned by CloudFront|
60+
|`caller_reference`|Reference used for this invalidation|
6161

6262
## 📋 Examples
6363
[View example →](./examples/base.yml)

actions/aws-lambda-restart/readme.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,28 +44,28 @@ jobs:
4444
```
4545
4646
## 📥 Inputs
47-
| **Name** | **Required** | **Description** | **Default** |
48-
|--------------------------|--------------|--------------------------------------------------------------------------------------|-------------|
49-
| `function_name` | ✅ Yes | Full Lambda function name | - |
50-
| `aws_region` | ✅ Yes | AWS region | - |
51-
| `aws_account_id` | ⚠️ Cond. | AWS account ID (12 digits) — required only when using `repository` + `image_tag` | - |
52-
| `aws_access_key_id` | ❌ No | AWS access key ID (optional if using OIDC) | - |
53-
| `aws_secret_access_key` | ❌ No | AWS secret access key (optional if using OIDC) | - |
54-
| `role_to_assume` | ❌ No | AWS IAM role ARN to assume (for OIDC authentication) | - |
55-
| `image_uri` | ❌ No | Full ECR image URI (overrides `repository`/`image_tag` when provided) | - |
56-
| `repository` | ❌ No | ECR repository name (used if `image_uri` not provided) | - |
57-
| `image_tag` | ❌ No | ECR image tag (used with `repository`) | `latest` |
58-
| `wait_for_update` | ❌ No | Wait for function update to complete (`true`/`false`) | `true` |
59-
| `show_summary` | ❌ No | Print summary with task output in job summary | `true` |
60-
| `summary_limit` | ❌ No | Max number of output lines to show in summary | `250` |
47+
|**Name**|**Required**|**Description**|**Default**|
48+
|---|---|---|---|
49+
|`function_name`|✅ Yes|Full Lambda function name|-|
50+
|`aws_region`|✅ Yes|AWS region|-|
51+
|`aws_account_id`|⚠️ Cond.|AWS account ID (12 digits) — required only when using `repository` + `image_tag`|-|
52+
|`aws_access_key_id`|❌ No|AWS access key ID (optional if using OIDC)|-|
53+
|`aws_secret_access_key`|❌ No|AWS secret access key (optional if using OIDC)|-|
54+
|`role_to_assume`|❌ No| AWS IAM role ARN to assume (for OIDC authentication)|-|
55+
|`image_uri`|❌ No|Full ECR image URI (overrides `repository`/`image_tag` when provided)|-|
56+
|`repository`|❌ No|ECR repository name (used if `image_uri` not provided)|-|
57+
|`image_tag`|❌ No|ECR image tag (used with `repository`)|`latest`|
58+
|`wait_for_update`|❌ No|Wait for function update to complete (`true`/`false`)|`true`|
59+
|`show_summary`|❌ No|Print summary with task output in job summary|`true`|
60+
|`summary_limit`|❌ No|Max number of output lines to show in summary|`250`|
6161

6262
## 📤 Outputs
63-
| **Name** | **Description** |
64-
|------------------|-----------------------------------------|
65-
| `function_arn` | Lambda function ARN |
66-
| `last_modified` | Function last modified timestamp |
67-
| `code_sha256` | Lambda code SHA256 |
68-
| `imgae_url` | Resolved image URI |
63+
|**Name**|**Description**|
64+
|---|---|
65+
|`function_arn`|Lambda function ARN|
66+
|`last_modified`|Function last modified timestamp|
67+
|`code_sha256`|Lambda code SHA256|
68+
|`imgae_url`|Resolved image URI|
6969

7070
## 📋 Examples
7171
[View example →](./examples/base.yml)

actions/aws-s3-sync/readme.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,31 +49,31 @@ jobs:
4949
```
5050

5151
## 📥 Inputs
52-
| **Name** | **Required** | **Description** | **Default** |
53-
|--------------------------|--------------|---------------------------------------------------------------------------------|----------------------------------------------|
54-
| `aws_region` | ✅ Yes | AWS region | - |
55-
| `bucket_name` | ✅ Yes | Target S3 bucket name | - |
56-
| `source_dir` | ✅ Yes | Local path to sync | - |
57-
| `aws_access_key` | ❌ No | AWS access key ID (optional if using OIDC) | - |
58-
| `aws_secret_key` | ❌ No | AWS secret access key (optional if using OIDC) | - |
59-
| `role_to_assume` | ❌ No | AWS IAM role ARN to assume (OIDC) | - |
60-
| `bucket_prefix` | ❌ No | Optional subpath prefix inside the bucket (trimmed of leading/trailing slashes) | `""` |
61-
| `delete_removed` | ❌ No | Remove objects in S3 that are not present in `source_dir` (`true`/`false`) | `true` |
62-
| `exclude_patterns` | ❌ No | Space‑separated exclude patterns passed to `aws s3 sync --exclude` | `.git/* .github/* .gitignore .gitattributes` |
63-
| `cache_control` | ❌ No | Value for `Cache-Control` header applied to uploads | - |
64-
| `content_type_detection` | ❌ No | Enable automatic content-type guessing based on file extension (true/false) | true |
65-
| `show_summary` | ❌ No | Print summary with task output in job summary | `true` |
66-
| `summary_limit` | ❌ No | Max number of output lines to show in summary | `250` |
52+
|**Name**|**Required**|**Description**|**Default**|
53+
|---|---|---|---|
54+
|`aws_region`|✅ Yes|AWS region|-|
55+
|`bucket_name`|✅ Yes|Target S3 bucket name|-|
56+
|`source_dir`|✅ Yes|Local path to sync|-|
57+
|`aws_access_key`|❌ No|AWS access key ID (optional if using OIDC)|-|
58+
|`aws_secret_key`|❌ No|AWS secret access key (optional if using OIDC)|-|
59+
|`role_to_assume`|❌ No|AWS IAM role ARN to assume (OIDC)|-|
60+
|`bucket_prefix`|❌ No|Optional subpath prefix inside the bucket (trimmed of leading/trailing slashes)|`""`|
61+
|`delete_removed`|❌ No|Remove objects in S3 that are not present in `source_dir` (`true`/`false`)|`true`|
62+
|`exclude_patterns`|❌ No|Space‑separated exclude patterns passed to `aws s3 sync --exclude`|`.git/* .github/* .gitignore .gitattributes`|
63+
|`cache_control`|❌ No|Value for `Cache-Control` header applied to uploads|-|
64+
|`content_type_detection`|❌ No|Enable automatic content-type guessing based on file extension (true/false)|true|
65+
|`show_summary`| ❌ No|Print summary with task output in job summary|`true`|
66+
|`summary_limit`|❌ No|Max number of output lines to show in summary|`250`|
6767

6868
## 📤 Outputs
69-
| **Name** | **Description** |
70-
|-------------------|-------------------------------------------------|
71-
| `files_uploaded` | Number of uploaded files |
72-
| `files_deleted` | Number of deleted files |
73-
| `total_size` | Total size in bytes of local files synced |
74-
| `file_count` | Total number of local files considered for sync |
75-
| `sync_duration` | Sync duration in seconds |
76-
| `s3_url` | Final S3 sync url |
69+
|**Name**|**Description**|
70+
|---|---|
71+
|`files_uploaded`|Number of uploaded files|
72+
|`files_deleted`|Number of deleted files|
73+
|`total_size`|Total size in bytes of local files synced|
74+
|`file_count`|Total number of local files considered for sync|
75+
|`sync_duration`|Sync duration in seconds|
76+
|`s3_url`|Final S3 sync url|
7777

7878
## 📋 Examples
7979
[View example →](./examples/base.yml)

0 commit comments

Comments
 (0)