没有不值得去解决的问题,也没有不值得去学习的技术!

From Syncing Go’s Official Upstream to Deploying 10 Language Sites: A Tour of Go Production Pipeline Converges Again

[Figure 7: Production Release Batch final summary]

作者:

This round of further optimization to the A Tour of Go multilingual project’s Production process had a very simple trigger: Go officially added links to the 4 translation sites I maintain to the official website.

After the French, German, Korean, and Simplified Chinese sites were added to the official A Tour of Go list of other languages, I naturally needed to sync the golang/website upstream again.

I originally thought this would just be a routine upstream sync.

Only after actually starting the execution did I realize that, with the number of currently live language sites increasing to 10, the task of “syncing the upstream once and then republishing all sites live” was itself worth further standardizing.

Ultimately, this maintenance effort further consolidated the previously scattered Production operations into a single command.

Starting point: Go officially added 4 translation site links

The official Go website repository merged _content/tour: add translations, adding four A Tour of Go translation entry points: French, German, Korean, and Simplified Chinese.

[Figure 1: Official Go golang/website commit, adding 4 translation site links]
[Figure 1: Official Go golang/website commit, adding 4 translation site links]

For me, this change also carried another layer of significance.

Previously, these language sites were just community translation sites that I maintained and promoted myself. After entering the official Go page, the official upstream itself began to include links pointing to these sites.

Therefore, the fork I maintain also had to be resynced with the upstream; otherwise, the project’s source baseline would fall behind the official one.

Subsequently, my repository completed two steps:

59b8b55:

chore: 同步官方 Go Tour 上游基线

and:

852f2cf:

chore: 完成官方上游同步后的多语言发布准备

[Figure 2: Official upstream sync and multilingual release preparation]
[Figure 2: Official upstream sync and multilingual release preparation]

As can be seen from the figure, this sync was not just a one-line link change.

In addition to upstream metadata and Tour pages, it also involved Locale Surface Review evidence, Production-related testing, and release preparation for different languages.

This is also an obvious change as the multilingual project grows larger: a very small upstream diff may eventually need to propagate to all official language sites.

The problem quickly shifted from “how to sync” to “how to republish all sites”

Syncing the upstream itself was not the most troublesome part of this round.

What really made me start continuing to optimize the process was the subsequent Production release.

The project currently has 10 official live locales:

Simplified Chinese, Japanese, German, French, Korean, Spanish, Italian, Dutch, Brazilian Portuguese, and Turkish.

If, after every change to the shared source code, we had to manually complete publish, deploy, CDN purge, machine acceptance, and browser acceptance one by one, then as the number of locales continues to grow, the maintenance cost will become increasingly apparent.

Moreover, this kind of work has a characteristic: the vast majority of steps do not require “thinking” again; they are essentially deterministic Production operations.

The real problem to solve therefore became:

How to preserve the already validated Production gates while consolidating the repetitive mechanical execution into a single official workflow?

The Git history from the past few days happens to record this evolution.

[Figure 3: Git commit timeline from upstream sync to Production batching]
[Figure 3: Git commit timeline from upstream sync to Production batching]

From the timeline, we can see:

First, sync the official Go Tour upstream, then complete the multilingual release preparation; next, start automated Production CDN purge and batch maintenance; then bring Production release and shared assets into the batch process; and finally, continue to refine batch recovery and shared assets verification.

This was not a “large and comprehensive” automation designed from the very beginning.

Instead, it was consolidated layer by layer during real Production usage.

Ultimately, only one official entry point is needed

Now, for all the live language sites, a complete maintenance release can be initiated with the following command:

Bash
scripts/production-release-batch.sh \
  --all-live \
  --output-root /tmp
[Figure 4: A single --all-live command initiates the complete batch release]
[Figure 4: A single --all-live command initiates the complete batch release]

In today’s actual run, the command first exported the shared assets:

Plaintext
files=14 bytes=971973

Next, it performed assets validation, and then automatically entered publish.

The 10 locales sequentially completed projection/build, production binary build, Chrome prerender, runtime validation, and manifest/checksum.

For example, each official language version would reconfirm:

Plaintext
ready=122
pending=0
blocked=0
pages=103
articles=7

The entire publish stage itself lasted for a considerable time. The logs ultimately recorded:

Plaintext
stage=publish PASS elapsed=1288.3s

This also shows that the so-called “one-click release” does not mean deleting the original strict process.

On the contrary, it organizes the formal steps that maintainers previously had to invoke manually and repeatedly into the same orchestration.

One command does not mean skipping Production gates

After the batch release, the process continues to perform official Production maintenance.

Phase A is responsible for deploy and CDN purge.

Taking the Korean site in the middle as an example, the deployment process still checks the release SHA-256, remote staging, permissions, remote SHA-256, systemd service health, consecutive HTTP 200s, and public access.

After deployment, it performs the CDN purge for the corresponding hostname.

Only then does it move on to the next locale.

[Figure 5: Phase A showing the current locale, stage, and overall progress]
[Figure 5: Phase A showing the current locale, stage, and overall progress]

Here, I later specifically continued to optimize something that seems very small but actually greatly affects the user experience: progress output.

If a long-running Production workflow only continuously prints curl, SSH, or deployment details, it is easy for the maintainer to not know:

Which language are we on right now?

Did the previous site finish?

How many are left?

Is the current step deploy, purge, or machine acceptance later on?

Therefore, it now explicitly outputs:

Plaintext
[phase A] locale=ko-KR 5/10 stage=purge PASS
[batch] phase=A completed=5/10 remaining=5
[phase A] locale=es-ES 6/10 stage=deploy START

This kind of output itself does not change the Production result, but for an official process that can last dozens of minutes, observability is very important.

Phase B still performs machine and browser acceptance site by site

Completing deploy and purge does not mean the release is over.

The subsequent Phase B continues to execute machine acceptance and browser acceptance.

Machine acceptance checks release identity, remote identity, official routes, CDN cache observation, HTML identity, sitemap, socket boundary, etc.

Browser acceptance continues to check desktop routes, mobile pages, as well as Run, Format, Reset, SPA, and ad-related behaviors.

In other words, the current “one-click” only reduces the maintainer’s repetitive command input; it does not simplify the acceptance criteria into a single sentence:

“An HTTP 200 on the homepage means the launch is successful.”

This is also the boundary I want this automation to always maintain.

The goal of automation is to reduce mechanical labor, not to lower Production standards.

A real curl exit 28 actually proved the value of the retry mechanism

Today’s round of 10-language official Production also had a very interesting little incident.

When it was the Dutch nl-NL‘s turn to execute sitemap verification, one of the URLs encountered this on the first request:

Plaintext
curl-exit-28
HTTP-000

The logs show:

Plaintext
attempt=1/5
reason=curl-exit-28
next=retry
backoff=1s

Subsequently, the second request recovered:

Plaintext
attempt=2/5 PASS

The final result was still:

Plaintext
sitemap URLs: 105/105
host mismatch: 0
HTTP failure: 0
PRODUCTION MACHINE ACCEPTANCE: PASS
[Figure 6: A real curl timeout for nl-NL, recovered after retry]
[Figure 6: A real curl timeout for nl-NL, recovered after retry]

This failure was not artificially created for testing, but a natural transient network issue that occurred in real Production.

This is also why I have been continuously refining the bounded retry.

The public internet, CDN, cross-border networks, and third-party APIs can never guarantee that every single request will succeed on the first try.

If a brief timeout immediately caused the entire 10-language batch to fail, it would generate a lot of false positives; but if the script unconditionally swallowed errors and moved on, it would break the fail-closed principle.

A more reasonable approach is to distinguish:

Brief network anomalies can be retried a limited number of times;

A successful recovery needs to explicitly record recovered;

If it ultimately still fails, the official gate still cannot pass.

This real curl exit 28 → recovered → PASS at least shows that the current direction has practical value.

Final result: all 10 languages completed

The final result of this complete maintenance release was:

[Figure 7: Production Release Batch final summary]

For the 10 locales:

deploy

purge

machine

browser

All PASS.

The final summary is:

Plaintext
PASS=10
FAILED=0
SKIPPED=0
PENDING=0

Shared assets were also completed:

Plaintext
export  PASS
deploy  DEPLOYED
purge   PASS
verify  PASS

Finally, we get:

Plaintext
PRODUCTION RELEASE BATCH: PASS
locale maintenance: COMPLETE

The final summary in the logs also listed the publish, deploy, purge, machine, and browser status for each locale separately. Therefore, even if a certain language fails in the future, it will be possible to know exactly which stage the failure occurred in, rather than just getting a generic “batch release failed”.

What this optimization really solved is not “typing fewer commands”

If it were just about typing a few fewer lines of shell, there would actually be no need to spend so much time perfecting this set of processes.

I value a few other things more.

As the number of language sites continues to increase, whether an operation can scale linearly will become increasingly important.

Right now, it is 10 locales.

If it grows to 20, 30, or even more in the future, then any step requiring the maintainer to “manually execute site by site” will gradually become a bottleneck.

Moreover, the more manual execution there is, the more likely another type of problem arises:

It’s not that the code has a bug, but that a certain language was forgotten during deploy, a certain CDN purge was forgotten, or a certain acceptance was not executed.

After consolidating these steps into an official workflow, the maintainer’s responsibilities begin to shift.

Previously, it was more like:

“Remember all the commands, and execute them correctly one by one.”

Now, it is more like:

“Start the official workflow, observe the explicit stages, progress, and failure evidence, and intervene when human judgment is truly needed.”

I think this is the more important value of this optimization.

The more automation there is, the more necessary it is to retain clear human boundaries

Working on the A Tour of Go multilingual project during this period, I have become increasingly inclined to divide the work into two categories.

One category is things machines are well-suited to do:

build, checksum, publish, deploy, CDN purge, HTTP verification, browser automation, status aggregation.

The other category is judgments that cannot be replaced by a machine just because automation is convenient:

Translation quality review, Production HUMAN visual gate, and the assessment of failure evidence upon encountering a real anomaly.

So I am not currently pursuing “doing everything in one command”.

What I want to achieve more is:

Reliably mechanize the parts that can be mechanized as much as possible;

Explicitly retain the gates that require human judgment.

These two do not conflict.

On the contrary, only when mechanical operations become increasingly stable can I devote more time to the areas truly worth human investment.

From a single upstream sync, continuing to drive the maturity of the entire Production process

In retrospect, the starting point of this whole matter was actually just Go officially adding 4 translation links.

I synced the upstream once.

Then, because I needed to republish all the language sites, I started re-examining the existing Production workflow.

Next, I successively refined CDN purge, batch maintenance, shared assets, recovery mechanisms, real-time progress, and failure diagnostics.

Ultimately, the 10 official language sites can start from a single command, automatically complete the entire maintenance release, and provide explicit per-locale acceptance results at the end.

This will probably not be the last evolution of this process.

In the future, as new locales continue to be added, new scaling problems will likely emerge.

But at least for now, “the official upstream has been updated again, we need to republish all official language sites” no longer means I have to manually repeat operations ten times.

For a project planned to scale to more languages long-term, this step is very important.

系列导航

需要长期技术维护或远程问题排查?

我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。

如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:

  • ✅ PHP / Laravel / Yii2 老项目无人维护
  • ✅ Go / Gin 后端接口需要排查或优化
  • ✅ WordPress 网站访问慢、报错或插件冲突
  • ✅ Nginx / MySQL / Redis / Linux 服务器异常
  • ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
  • ✅ 需要长期远程技术支持或兼职维护

更多介绍请查看:关于我 & 合作

微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan