Skip to content

Commit 2e4d5c5

Browse files
Merge pull request #247 from circleci/fix-bucket
fix: update S3 bucket paths for docs preview deployment
2 parents 3deb3d2 + 122a368 commit 2e4d5c5

File tree

6 files changed

+317
-43
lines changed

6 files changed

+317
-43
lines changed

.circleci/config.yml

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ executors:
1515
- image: cimg/node:22.15.1
1616
working_directory: ~/project
1717

18+
python_executor:
19+
docker:
20+
- image: cimg/python:3.12.11
21+
working_directory: ~/project
22+
1823
ruby_executor:
1924
docker:
2025
- image: cimg/ruby:3.4
@@ -185,7 +190,7 @@ jobs:
185190
message: "Deploy preview job failed for branch ${CIRCLE_BRANCH}"
186191

187192
deploy-production:
188-
executor: node_executor
193+
executor: python_executor
189194
parameters:
190195
bucket_name:
191196
description: The name of the s3 bucket where static assets are stored.
@@ -208,13 +213,26 @@ jobs:
208213
echo "[INFO] Deploying production site..."
209214
aws s3 sync "$BUILD_DIRECTORY" "s3://$AWS_S3_BUCKET/"
210215
- run:
211-
name: Deploy Single Test Redirect to S3
216+
name: install pyyaml requests
217+
command: |
218+
set -e
219+
echo "[INFO] Installing pyyaml requests..."
220+
pip install pyyaml requests
221+
- run:
222+
name: Deploy Redirects to S3
212223
command: |
213224
AWS_S3_BUCKET=<< parameters.bucket_name >>
214225
215226
set -e
216-
echo "[INFO] Deploying single test redirect..."
217-
bash scripts/test-single-redirect.sh "$AWS_S3_BUCKET"
227+
echo "[INFO] Deploying redirects..."
228+
#REMEMBER TO UPDATE THE START/END PARAMETER IN THE VALIDATE JOB TO MATCH
229+
python scripts/create-redirects.py $AWS_S3_BUCKET --start 0 --end 10
230+
- run:
231+
name: Validate Redirects
232+
command: |
233+
set -e
234+
echo "[INFO] Validating redirects..."
235+
python scripts/validate-redirects.py https://circleci.com/docs-preview --start 0 --end 10
218236
- notify_error:
219237
message: "Production deployment job failed for branch ${CIRCLE_BRANCH}"
220238

@@ -299,7 +317,7 @@ workflows:
299317
web-ui-npm,
300318
web-ui-datadog,
301319
]
302-
bucket_name: "circleci-docs-platform-assets/docs-preview"
320+
bucket_name: "circleci-docs-platform-assets"
303321
build_dir: "build"
304322
cleanup_preview:
305323
when: pipeline.parameters.cleanup_preview_branch != ""

scripts/create-redirect.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Validate parameters
5+
if [ "$#" -ne 3 ]; then
6+
echo "Usage: $0 <bucket_name> <old_path> <new_path>"
7+
echo "Example: $0 circleci-docs-platform-assets /about-circleci/ /docs-preview/guides/about-circleci/index.html"
8+
exit 1
9+
fi
10+
11+
DESTINATION_FOLDER="docs-preview"
12+
13+
BUCKET_NAME="$1"
14+
OLD_PATH="$2" # must include leading and trailing slash e.g. /about-circleci/
15+
NEW_PATH="/${DESTINATION_FOLDER}$3" # full target path e.g. /docs-preview/guide/about-circleci/index.html
16+
17+
echo "[INFO] Deploying redirect to bucket: s3://$BUCKET_NAME"
18+
echo "[INFO] Redirect: $OLD_PATH -> $NEW_PATH"
19+
20+
# Convert old path to S3 key (no leading slash before key!)
21+
S3_KEY="${DESTINATION_FOLDER}${OLD_PATH}index.html"
22+
23+
echo "[INFO] Creating S3 object: $S3_KEY"
24+
25+
# Create the redirect object in S3
26+
aws s3api put-object \
27+
--bucket "$BUCKET_NAME" \
28+
--key "$S3_KEY" \
29+
--website-redirect-location "$NEW_PATH" \
30+
--content-type "text/html" \
31+
--content-length "0"
32+
33+
echo "[INFO] ✅ Redirect created successfully!"

scripts/create-redirects.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/usr/bin/env python3
2+
"""Bulk S3 redirect creator.
3+
4+
Reads redirects from scripts/redirects_v2.yml (expects list of mapping with
5+
`old` and `new` keys) and calls `scripts/create-redirect.sh` for each redirect.
6+
7+
Usage:
8+
python scripts/create-redirect.py <bucket_name> [--start N] [--end M]
9+
10+
Options:
11+
--start N Start index (inclusive) in redirects list to process. Default 0.
12+
--end M End index (exclusive). Default len(list).
13+
14+
The slice options are useful for testing a subset.
15+
"""
16+
from __future__ import annotations
17+
18+
import argparse
19+
import subprocess
20+
import sys
21+
from pathlib import Path
22+
23+
try:
24+
import yaml # type: ignore
25+
except ImportError: # pragma: no cover
26+
sys.stderr.write("[ERROR] PyYAML is required. Install with: pip install pyyaml\n")
27+
sys.exit(1)
28+
29+
ROOT_DIR = Path(__file__).resolve().parent.parent # repo root (circleci-docs-static)
30+
REDIRECTS_FILE = ROOT_DIR / "scripts" / "redirects_v2.yml"
31+
SH_SCRIPT = ROOT_DIR / "scripts" / "create-redirect.sh"
32+
33+
34+
def load_redirects(path: Path) -> list[dict[str, str]]:
35+
"""Load redirects YAML file into list of dicts with 'old' and 'new'."""
36+
if not path.exists():
37+
raise FileNotFoundError(f"Redirects YAML not found: {path}")
38+
39+
with path.open("r", encoding="utf-8") as fh:
40+
data = yaml.safe_load(fh)
41+
42+
if not isinstance(data, list):
43+
raise ValueError("Redirects YAML should be a list of mappings.")
44+
45+
redirects: list[dict[str, str]] = []
46+
for item in data:
47+
if not isinstance(item, dict):
48+
continue
49+
old = item.get("old")
50+
new = item.get("new")
51+
if old and new:
52+
redirects.append({"old": str(old), "new": str(new)})
53+
return redirects
54+
55+
56+
def create_redirect(bucket: str, old_path: str, new_path: str) -> None:
57+
"""Call the shell script to create a single redirect."""
58+
cmd = [
59+
"bash",
60+
str(SH_SCRIPT),
61+
bucket,
62+
old_path,
63+
new_path,
64+
]
65+
subprocess.run(cmd, check=True)
66+
67+
68+
def main() -> None:
69+
parser = argparse.ArgumentParser(description="Bulk S3 redirect creator")
70+
parser.add_argument("bucket", help="Target S3 bucket name")
71+
parser.add_argument("--start", type=int, default=0, help="Start index (inclusive)")
72+
parser.add_argument("--end", type=int, default=None, help="End index (exclusive)")
73+
args = parser.parse_args()
74+
75+
redirects = load_redirects(REDIRECTS_FILE)
76+
end = args.end if args.end is not None else len(redirects)
77+
78+
slice_redirects = redirects[args.start : end]
79+
total = len(slice_redirects)
80+
print(f"[INFO] Processing {total} redirects (indexes {args.start}-{end-1})...")
81+
82+
for idx, entry in enumerate(slice_redirects, start=args.start):
83+
old = entry["old"]
84+
new = entry["new"]
85+
try:
86+
print(f"[INFO] ({idx}) Creating redirect {old} -> {new}")
87+
create_redirect(args.bucket, old, new)
88+
except subprocess.CalledProcessError as exc:
89+
print(f"[ERROR] Failed to create redirect for {old}: {exc}", file=sys.stderr)
90+
91+
92+
if __name__ == "__main__":
93+
main()

scripts/redirects_v2.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
# Total redirects: 502
44

55
- old: /about-circleci/
6-
new: /guides/about-circleci/about-circleci/index.html
6+
new: /guides/about-circleci/index.html
77

88
- old: /add-ssh-key/
9-
new: /guides/integration/add-ssh-key/index.html
9+
new: /guides/integration/add-ssh-key.html
1010

1111
- old: /android-images-support-policy/
1212
new: /guides/execution-managed/android-images-support-policy/index.html

scripts/test-single-redirect.sh

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)