Skip to content

Commit

Permalink
feat(.github): support dynamic submodule detection (#4537)
Browse files Browse the repository at this point in the history
Today the sub-modules are are coded into a couple of places to
work with release-please. This change is the first of three to
make release-please automatically support newly added modules.
  • Loading branch information
codyoss authored Aug 5, 2021
1 parent 67facd9 commit 4374b90
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 37 deletions.
42 changes: 5 additions & 37 deletions .github/workflows/release-submodule.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,44 +19,12 @@ jobs:
submodules: ${{ steps.interrogate.outputs.submodules }}
steps:
- uses: actions/checkout@v2
- id: interrogate
uses: actions/github-script@v4
- name: Setup Go
uses: actions/setup-go@v2
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const {execSync} = require('child_process');
const SUB_MODULES = [
'bigtable',
'bigquery',
'datastore',
'firestore',
'logging',
'pubsub',
'pubsublite',
'spanner',
'storage',
];
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
const latestRelease = await github.repos.getLatestRelease({
owner,
repo
});
console.info(`latest release: ${latestRelease.data.tag_name}`);
// pull all tags, so we can get diff between HEAD and last release:
execSync('git pull --tags');
execSync(`git reset --hard ${latestRelease.data.tag_name}`);
const status = execSync(`git diff --name-only origin/master`, { encoding: 'utf-8'});
console.info(status);
const changes = status.split('\n');
const submodules = new Set();
for (const change of changes) {
const library = change.split('/')[0];
console.info(`update to path ${library}`);
if (SUB_MODULES.includes(library)) {
submodules.add(library);
}
}
console.log(`::set-output name=submodules::${JSON.stringify(Array.from(submodules))}`);
go-version: '^1.16'
- id: interrogate
run: go run internal/actions/cmd/changefinder/main.go
release-pr: # Create the release PR based on commit history:
runs-on: ubuntu-latest
needs: changeFinder
Expand Down
152 changes: 152 additions & 0 deletions internal/actions/cmd/changefinder/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"bytes"
"encoding/json"
"fmt"
"io/fs"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
)

func main() {
rootDir, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
if len(os.Args) > 1 {
rootDir = os.Args[1]
}
log.Printf("Root dir: %q", rootDir)
var modDirs []string
// Find all external modules
filepath.WalkDir(rootDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.Name() == "internal" {
return filepath.SkipDir
}
if d.Name() == "go.mod" {
modDirs = append(modDirs, filepath.Dir(path))
}
return nil
})

// Find relative sub-module
submodules := map[string]bool{}
for _, dir := range modDirs {
name, err := modName(dir)
if err != nil {
log.Fatalf("unable to lookup mod dir")
}
// Skip non-submodule
if name == "cloud.google.com/go" {
continue
}
name = strings.TrimPrefix(name, "cloud.google.com/go/")
submodules[name] = true
}

c := exec.Command("git", "pull", "--tags")
c.Dir = rootDir
if err := c.Run(); err != nil {
log.Fatalf("unable to pull tags: %v", err)
}

tag, err := latestTag(rootDir)
if err != nil {
log.Fatalf("unable to find tag: %v", err)
}
log.Printf("Latest release: %s", tag)

c = exec.Command("git", "reset", "--hard", tag)
c.Dir = rootDir
if err := c.Run(); err != nil {
log.Fatalf("unable to reset to tag: %v", err)
}

changes, err := gitFilesChanges(rootDir)
if err != nil {
log.Fatalf("unable to get files changed: %v", err)
}

updatedSubmodulesSet := map[string]bool{}
for _, change := range changes {
//TODO(codyoss): This will not work with nested sub-modules. If we add
// those this needs to be updated.
pkg := strings.Split(change, "/")[0]
log.Printf("update to path: %s", pkg)
if submodules[pkg] {
updatedSubmodulesSet[pkg] = true
}
}

updatedSubmodule := []string{}
for mod := range updatedSubmodulesSet {
updatedSubmodule = append(updatedSubmodule, mod)
}
b, err := json.Marshal(updatedSubmodule)
if err != nil {
log.Fatalf("unable to marshal submodules: %v", err)
}
fmt.Printf("::set-output name=submodules::%s", b)
}

func modName(dir string) (string, error) {
c := exec.Command("go", "list", "-m")
c.Dir = dir
b, err := c.Output()
if err != nil {
return "", err
}
b = bytes.TrimSpace(b)
return string(b), nil
}

func latestTag(dir string) (string, error) {
c := exec.Command("git", "rev-list", "--tags", "--max-count=1")
c.Dir = dir
b, err := c.Output()
if err != nil {
return "", err
}
commit := string(bytes.TrimSpace(b))
c = exec.Command("git", "describe", "--tags", commit)
c.Dir = dir
b, err = c.Output()
if err != nil {
return "", err
}
b = bytes.TrimSpace(b)
return string(b), nil
}

func gitFilesChanges(dir string) ([]string, error) {
c := exec.Command("git", "diff", "--name-only", "origin/master")
c.Dir = dir
b, err := c.Output()
if err != nil {
return nil, err
}
b = bytes.TrimSpace(b)
log.Printf("Files changed:\n%s", b)
return strings.Split(string(b), "\n"), nil
}
3 changes: 3 additions & 0 deletions internal/actions/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module cloud.google.com/go/internal/actions

go 1.16

0 comments on commit 4374b90

Please sign in to comment.