Skip to content

Commit 6bad499

Browse files
committed
[TT-17973] Render the public retired-versions table from the plan
The pkgs retirement subcommand turns the committed retention plan into the retired-versions snippet published on tyk.io, and the retention plan workflow PRs it to tyk-docs whenever the table changes. Rendering from the plan rather than recomputing guarantees the published table matches what pruning will enforce. The hand-written first draft of the table proved the point: it said 5.8.x where the derived cutoff is actually 5.5.x, because the EE window keeps three released series below the previous LTS.
1 parent 8345df5 commit 6bad499

4 files changed

Lines changed: 152 additions & 1 deletion

File tree

.github/workflows/retention-plan.yml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ jobs:
3737
app-id: ${{ secrets.PROBE_APP_ID }}
3838
private-key: ${{ secrets.PROBE_APP_PRIVATE_KEY }}
3939
owner: TykTechnologies
40-
repositories: artifact-retention-plans
40+
repositories: artifact-retention-plans,tyk-docs
4141
permission-contents: write
42+
permission-pull-requests: write
4243

4344
- name: Checkout gromit
4445
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@@ -131,6 +132,32 @@ jobs:
131132
echo "Nothing was deleted by this workflow."
132133
} >> "$GITHUB_STEP_SUMMARY"
133134
135+
# No PR is opened when the table is unchanged
136+
- name: Checkout tyk-docs
137+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
138+
with:
139+
repository: TykTechnologies/tyk-docs
140+
token: ${{ steps.app-token.outputs.token }}
141+
path: tyk-docs
142+
persist-credentials: false
143+
144+
- name: Render retired-versions snippet
145+
run: go run . pkgs retirement --plan plan.json > tyk-docs/snippets/retired-versions.mdx
146+
147+
- name: PR the snippet to tyk-docs
148+
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
149+
with:
150+
token: ${{ steps.app-token.outputs.token }}
151+
path: tyk-docs
152+
branch: gromit/retired-versions
153+
delete-branch: true
154+
commit-message: 'Update retired versions table'
155+
title: 'Update retired versions table'
156+
body: |
157+
Regenerated from the latest [retention plan](https://github.com/TykTechnologies/artifact-retention-plans/commit/${{ steps.commit.outputs.sha }}) by [this run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}).
158+
159+
The table in `snippets/retired-versions.mdx` reflects the retention cutoffs the pruning run will enforce.
160+
134161
- name: Slack notice
135162
if: steps.diff.outputs.new_count != '0'
136163
uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3.0.1

cmd/pkgs.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ var pkgsCmd = &cobra.Command{
3939
4040
You can perform maintenance using this command tree.`,
4141
PersistentPreRun: func(cmd *cobra.Command, args []string) {
42+
// retirement works offline, from a plan file
43+
if cmd.Name() == "retirement" {
44+
return
45+
}
4246
pcToken := os.Getenv("PACKAGECLOUD_TOKEN")
4347
if pcToken == "" {
4448
log.Fatal().Msg("Working with packagecloud.io requires PACKAGECLOUD_TOKEN")
@@ -215,10 +219,35 @@ plan must not proceed to deletion.`,
215219
},
216220
}
217221

222+
var retirementSubCmd = &cobra.Command{
223+
Use: "retirement",
224+
Short: "Render the public retired-versions table from a plan",
225+
Long: `Reads a plan (the JSON from 'pkgs plan --json') and emits the
226+
retired-versions snippet published on the tyk.io retention policy
227+
page. Only track-driven repos appear in the table.
228+
229+
Rendering from the committed plan rather than recomputing keeps the
230+
published table identical to what the pruning run will enforce.`,
231+
RunE: func(cmd *cobra.Command, args []string) error {
232+
planFile, _ := cmd.Flags().GetString("plan")
233+
data, err := os.ReadFile(planFile)
234+
if err != nil {
235+
return err
236+
}
237+
var plans []pkgs.Plan
238+
if err := json.Unmarshal(data, &plans); err != nil {
239+
return fmt.Errorf("parsing %s: %w", planFile, err)
240+
}
241+
fmt.Fprint(cmd.OutOrStdout(), pkgs.RenderRetiredVersions(plans))
242+
return nil
243+
},
244+
}
245+
218246
func init() {
219247
pkgsCmd.AddCommand(cleanSubCmd)
220248
pkgsCmd.AddCommand(planSubCmd)
221249
pkgsCmd.AddCommand(mirrorSubCmd)
250+
pkgsCmd.AddCommand(retirementSubCmd)
222251
rootCmd.AddCommand(pkgsCmd)
223252

224253
pkgsCmd.PersistentFlags().String("owner", "tyk", "PackageCloud repo owner")
@@ -238,4 +267,7 @@ func init() {
238267
mirrorSubCmd.Flags().String("bucket", "tyk-artifact-archive", "S3 bucket to archive to")
239268
mirrorSubCmd.Flags().Bool("verify", false, "Read every archived object back and check its hash")
240269
mirrorSubCmd.Flags().Int("concurrency", 3, "Repos to mirror in parallel")
270+
271+
retirementSubCmd.Flags().String("plan", "", "Plan file from 'pkgs plan --json'")
272+
retirementSubCmd.MarkFlagRequired("plan")
241273
}

pkgs/retire.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package pkgs
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
"strings"
7+
8+
"golang.org/x/mod/semver"
9+
)
10+
11+
var productNames = map[string]string{
12+
"tyk-gateway": "Tyk Gateway",
13+
"tyk-dashboard": "Tyk Dashboard",
14+
"tyk-pump": "Tyk Pump",
15+
"tyk-mdcb": "Tyk MDCB",
16+
}
17+
18+
// ProductName returns the display name for a repo
19+
func ProductName(repo string) string {
20+
if name, found := productNames[repo]; found {
21+
return name
22+
}
23+
words := strings.Split(repo, "-")
24+
for i, w := range words {
25+
if w == "" {
26+
continue
27+
}
28+
words[i] = strings.ToUpper(w[:1]) + w[1:]
29+
}
30+
return strings.Join(words, " ")
31+
}
32+
33+
// RenderRetiredVersions renders the tyk-docs retirement table from a
34+
// plan. Only track-driven repos appear, so the published table cannot
35+
// disagree with what pruning will do.
36+
func RenderRetiredVersions(plans []Plan) string {
37+
var b strings.Builder
38+
b.WriteString(`{/* This table is generated by gromit from the release tracks
39+
configuration. Do not edit it by hand: changes will be
40+
overwritten by the next sync. */}
41+
42+
| Product | Retained from | Retired below |
43+
|---------|---------------|---------------|
44+
`)
45+
rows := make([]string, 0, len(plans))
46+
for _, p := range plans {
47+
if p.Track == "" || p.Cutoff == "" {
48+
continue
49+
}
50+
series := strings.TrimPrefix(semver.MajorMinor(p.Cutoff), "v")
51+
oldest := strings.TrimPrefix(semver.Canonical(p.Cutoff), "v")
52+
rows = append(rows, fmt.Sprintf("| %s | %s.x | %s |\n", ProductName(p.Repo), series, oldest))
53+
}
54+
sort.Strings(rows)
55+
for _, r := range rows {
56+
b.WriteString(r)
57+
}
58+
return b.String()
59+
}

pkgs/retire_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package pkgs
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestRenderRetiredVersions(t *testing.T) {
10+
plans := []Plan{
11+
{Repo: "tyk-gateway", Track: "gateway", Cutoff: "v5.5"},
12+
{Repo: "tyk-dashboard", Track: "gateway", Cutoff: "v5.5"},
13+
// not track-driven, must not appear
14+
{Repo: "tyk-pump", Cutoff: "v1.0.0"},
15+
{Repo: "tyk-identity-broker"},
16+
}
17+
want := `{/* This table is generated by gromit from the release tracks
18+
configuration. Do not edit it by hand: changes will be
19+
overwritten by the next sync. */}
20+
21+
| Product | Retained from | Retired below |
22+
|---------|---------------|---------------|
23+
| Tyk Dashboard | 5.5.x | 5.5.0 |
24+
| Tyk Gateway | 5.5.x | 5.5.0 |
25+
`
26+
assert.Equal(t, want, RenderRetiredVersions(plans))
27+
}
28+
29+
func TestProductName(t *testing.T) {
30+
assert.Equal(t, "Tyk Gateway", ProductName("tyk-gateway"))
31+
assert.Equal(t, "Tyk MDCB", ProductName("tyk-mdcb"))
32+
assert.Equal(t, "Tyk Sync", ProductName("tyk-sync"))
33+
}

0 commit comments

Comments
 (0)