Skip to content

Commit

Permalink
Update release instructions for documentation. (tendermint#7846)
Browse files Browse the repository at this point in the history
* Add linkpatch tool to rewrite documentation URLs.
* Update instructions for creating backport branches.

Updates tendermint#7675.
  • Loading branch information
M. J. Fromberger authored Feb 21, 2022
1 parent 351adf8 commit bd6fce1
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 2 deletions.
29 changes: 28 additions & 1 deletion RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,42 @@ In the following example, we'll assume that we're making a backport branch for
the 0.35.x line.

1. Start on `master`

2. Create and push the backport branch:
```sh
git checkout -b v0.35.x
git push origin v0.35.x
```

3. Create a PR to update the documentation directory for the backport branch.

We only maintain RFC and ADR documents on master, to avoid confusion.
In addition, we rewrite Markdown URLs pointing to master to point to the
backport branch, so that generated documentation will link to the correct
versions of files elsewhere in the repository. For context on the latter,
see https://github.com/tendermint/tendermint/issues/7675.

To prepare the PR:
```sh
# Remove the RFC and ADR documents from the backport.
# We only maintain these on master to avoid confusion.
git rm -r docs/rfc docs/architecture

# Update absolute links to point to the backport.
go run ./scripts/linkpatch -recur -target v0.35.x -skip-path docs/DOCS_README.md,docs/README.md docs

# Create and push the PR.
git checkout -b update-docs-v035x
git commit -m "Update docs for v0.35.x backport branch." docs
git push -u origin update-docs-v035x
```

Be sure to merge this PR before making other changes on the newly-created
backport branch.

After doing these steps, go back to `master` and do the following:

1. Tag `master` as the dev branch for the _next_ major release and push it back up.
1. Tag `master` as the dev branch for the _next_ major release and push it up to GitHub.
For example:
```sh
git tag -a v0.36.0-dev -m "Development base for Tendermint v0.36."
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Tendermint?](introduction/what-is-tendermint.md).

To get started quickly with an example application, see the [quick start guide](introduction/quick-start.md).

To learn about application development on Tendermint, see the [Application Blockchain Interface](https://github.com/tendermint/tendermint/tree/master/spec/abci).
To learn about application development on Tendermint, see the [Application Blockchain Interface](../spec/abci).

For more details on using Tendermint, see the respective documentation for
[Tendermint Core](tendermint-core/), [benchmarking and monitoring](tools/), and [network deployments](nodes/).
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ require (
github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af // indirect
github.com/containerd/continuity v0.2.1 // indirect
github.com/daixiang0/gci v0.3.1-0.20220208004058-76d765e3ab48 // indirect
github.com/creachadair/atomicfile v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denis-tingajkin/go-header v0.4.2 // indirect
github.com/dgraph-io/badger/v2 v2.2007.2 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creachadair/atomicfile v0.2.4 h1:GRjpQLmz/78I4+nBQpGMFrRa9yrL157AUTrA6hnF0YU=
github.com/creachadair/atomicfile v0.2.4/go.mod h1:BRq8Une6ckFneYXZQ+kO7p1ZZP3I2fzVzf28JxrIkBc=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
Expand Down
205 changes: 205 additions & 0 deletions scripts/linkpatch/linkpatch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// Program linkpatch rewrites absolute URLs pointing to targets in GitHub in
// Markdown link tags to target a different branch.
//
// This is used to update documentation links for backport branches.
// See https://github.com/tendermint/tendermint/issues/7675 for context.
package main

import (
"bytes"
"flag"
"fmt"
"io/fs"
"log"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/creachadair/atomicfile"
)

var (
repoName = flag.String("repo", "tendermint/tendermint", "Repository name to match")
sourceBranch = flag.String("source", "master", "Source branch name (required)")
targetBranch = flag.String("target", "", "Target branch name (required)")
doRecur = flag.Bool("recur", false, "Recur into subdirectories")

skipPath stringList
skipMatch regexpFlag

// Match markdown links pointing to absolute URLs.
// This only works for "inline" links, not referenced links.
// The submetch selects the URL.
linkRE = regexp.MustCompile(`(?m)\[.*?\]\((https?://.*?)\)`)
)

func init() {
flag.Var(&skipPath, "skip-path", "Skip these paths (comma-separated)")
flag.Var(&skipMatch, "skip-match", "Skip URLs matching this regexp (RE2)")

flag.Usage = func() {
fmt.Fprintf(os.Stderr, `Usage: %[1]s [options] <file/dir>...
Rewrite absolute Markdown links targeting the specified GitHub repository
and source branch name to point to the target branch instead. Matching
files are updated in-place.
Each path names either a directory to list, or a single file path to
rewrite. By default, only the top level of a directory is scanned; use -recur
to recur into subdirectories.
Options:
`, filepath.Base(os.Args[0]))
flag.PrintDefaults()
}
}

func main() {
flag.Parse()
switch {
case *repoName == "":
log.Fatal("You must specify a non-empty -repo name (org/repo)")
case *targetBranch == "":
log.Fatal("You must specify a non-empty -target branch")
case *sourceBranch == "":
log.Fatal("You must specify a non-empty -source branch")
case *sourceBranch == *targetBranch:
log.Fatalf("Source and target branch are the same (%q)", *sourceBranch)
case flag.NArg() == 0:
log.Fatal("You must specify at least one file/directory to rewrite")
}

r, err := regexp.Compile(fmt.Sprintf(`^https?://github.com/%s/(?:blob|tree)/%s`,
*repoName, *sourceBranch))
if err != nil {
log.Fatalf("Compiling regexp: %v", err)
}
for _, path := range flag.Args() {
if err := processPath(r, path); err != nil {
log.Fatalf("Processing %q failed: %v", path, err)
}
}
}

func processPath(r *regexp.Regexp, path string) error {
fi, err := os.Lstat(path)
if err != nil {
return err
}
if fi.Mode().IsDir() {
return processDir(r, path)
} else if fi.Mode().IsRegular() {
return processFile(r, path)
}
return nil // nothing to do with links, device files, sockets, etc.
}

func processDir(r *regexp.Regexp, root string) error {
return filepath.Walk(root, func(path string, fi fs.FileInfo, err error) error {
if err != nil {
return err
}
if fi.IsDir() {
if skipPath.Contains(path) {
log.Printf("Skipping %q (per -skip-path)", path)
return filepath.SkipDir // explicitly skipped
} else if !*doRecur && path != root {
return filepath.SkipDir // skipped because we aren't recurring
}
return nil // nothing else to do for directories
} else if skipPath.Contains(path) {
log.Printf("Skipping %q (per -skip-path)", path)
return nil // explicitly skipped
} else if filepath.Ext(path) != ".md" {
return nil // nothing to do for non-Markdown files
}

return processFile(r, path)
})
}

func processFile(r *regexp.Regexp, path string) error {
log.Printf("Processing file %q", path)
input, err := os.ReadFile(path)
if err != nil {
return err
}

pos := 0
var output bytes.Buffer
for _, m := range linkRE.FindAllSubmatchIndex(input, -1) {
href := string(input[m[2]:m[3]])
u := r.FindStringIndex(href)
if u == nil || skipMatch.MatchString(href) {
if u != nil {
log.Printf("Skipped URL %q (by -skip-match)", href)
}
output.Write(input[pos:m[1]]) // copy the existing data as-is
pos = m[1]
continue
}

// Copy everything before the URL as-is, then write the replacement.
output.Write(input[pos:m[2]]) // everything up to the URL
fmt.Fprintf(&output, `https://github.com/%s/blob/%s%s`, *repoName, *targetBranch, href[u[1]:])

// Write out the tail of the match, everything after the URL.
output.Write(input[m[3]:m[1]])
pos = m[1]
}
output.Write(input[pos:]) // the rest of the file

_, err = atomicfile.WriteAll(path, &output, 0644)
return err
}

// stringList implements the flag.Value interface for a comma-separated list of strings.
type stringList []string

func (lst *stringList) Set(s string) error {
if s == "" {
*lst = nil
} else {
*lst = strings.Split(s, ",")
}
return nil
}

// Contains reports whether lst contains s.
func (lst stringList) Contains(s string) bool {
for _, elt := range lst {
if s == elt {
return true
}
}
return false
}

func (lst stringList) String() string { return strings.Join([]string(lst), ",") }

// regexpFlag implements the flag.Value interface for a regular expression.
type regexpFlag struct{ *regexp.Regexp }

func (r regexpFlag) MatchString(s string) bool {
if r.Regexp == nil {
return false
}
return r.Regexp.MatchString(s)
}

func (r *regexpFlag) Set(s string) error {
c, err := regexp.Compile(s)
if err != nil {
return err
}
r.Regexp = c
return nil
}

func (r regexpFlag) String() string {
if r.Regexp == nil {
return ""
}
return r.Regexp.String()
}

0 comments on commit bd6fce1

Please sign in to comment.