Skip to content

Commit

Permalink
feat: add go_version_file support
Browse files Browse the repository at this point in the history
Add support for determining the version of buf to use from go.mod
including full test for the functionality.

This allows go projects to have a single source for go module versions.

Also:
* Fixes from lint issues in README.md.

Fixes #155
  • Loading branch information
stevenh committed Aug 15, 2023
1 parent eb60cd0 commit ad16c02
Show file tree
Hide file tree
Showing 13 changed files with 10,936 additions and 2,577 deletions.
50 changes: 45 additions & 5 deletions .eslintrc.js
@@ -1,6 +1,6 @@
// Copyright 2020-2022 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// 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
//
Expand All @@ -12,14 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const ignoreFiles = [".eslintrc.js", "dist/**/*"];
const ignoreFiles = ["dist/**/*", ".eslintrc.js", "jest.config.js"];

module.exports = {
env: {
es2022: true,
"jest/globals": true,
},
ignorePatterns: ignoreFiles,
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:eslint-plugin-jest/recommended",
"eslint-config-prettier",
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "tsconfig.json",
Expand All @@ -30,6 +36,40 @@ module.exports = {
ecmaVersion: 12,
sourceType: "module",
},
plugins: ["@typescript-eslint"],
rules: {},
plugins: ["@typescript-eslint", "eslint-plugin-node", "eslint-plugin-jest"],
rules: {
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/ban-ts-comment": [
"error",
{
"ts-ignore": "allow-with-description",
},
],
"no-console": "error",
yoda: "error",
"prefer-const": [
"error",
{
destructuring: "all",
},
],
"no-control-regex": "off",
"no-constant-condition": ["error", { checkLoops: false }],
"node/no-extraneous-import": "error",
},
overrides: [
{
files: ["**/*{test,spec}.ts"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"jest/no-standalone-expect": "off",
"jest/no-conditional-expect": "off",
"no-console": "off",
},
},
],
};
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -2,3 +2,4 @@
.tmp/
.vscode/
node_modules/
coverage/
6 changes: 5 additions & 1 deletion Makefile
Expand Up @@ -30,8 +30,12 @@ format: node_modules
lint: node_modules
npm run lint

.PHONY: test
test: node_modules
npm run test

.PHONY: build
build: node_modules format lint
build: node_modules format lint test
npm run build

.PHONY: updateversion
Expand Down
30 changes: 15 additions & 15 deletions README.md
@@ -1,7 +1,7 @@
# `buf-setup-action`

This [Action] installs the [`buf`][buf-cli] CLI in your GitHub Actions pipelines so that it can be
used by other Buf Actions:
This [action] installs the [`buf`][buf-cli] CLI in your GitHub Actions
pipelines so that it can be used by other Buf Actions:

* [`buf-breaking-action`][buf-breaking]
* [`buf-lint-action`][buf-lint]
Expand Down Expand Up @@ -30,15 +30,17 @@ steps:

You can configure `buf-setup-action` with these parameters:

| Parameter | Description | Default |
|:---------------|:---------------------------------------------------|:-------------------|
| `version` | The version of the [`buf` CLI][buf-cli] to install | [`v1.26.1`][version] |
| `github_token` | The GitHub token to use when making API requests | |
| `buf_user` | The username to use for logging into Buf Schema registry. | |
| `buf_api_token` | The API token to use for logging into Buf Schema registry. | |
| `buf_domain` | The domain of the Buf Schema Registry to login to. | buf.build |
| Parameter | Description | Default |
|:------------------|:------------------------------------------------------------|:---------------------|
| `version` | The version of the [`buf` CLI][buf-cli] to install | [`v1.26.1`][version] |
| `github_token` | The GitHub token to use when making API requests | |
| `buf_user` | The username to use for logging into Buf Schema registry. | |
| `buf_api_token` | The API token to use for logging into Buf Schema registry. | |
| `buf_domain` | The domain of the Buf Schema Registry to login to. | buf.build |
| `go_version_file` | The go.mod file to read the buf version from. | |

> These parameters are derived from [`action.yml`](./action.yml).
> These parameters are derived from [`action.yml`](./action.yml). <br>
#### Version

If `version` is unspecified, the latest version of `buf` is installed:
Expand Down Expand Up @@ -102,7 +104,7 @@ steps:

#### Buf username and Buf API token

If you are using Private [Remote Packages](https://docs.buf.build/bsr/remote-packages/overview) you may need to authenticate the entire system to successfully communicate with the [Buf Schema Registry][bsr]. To achieve this, supply both `buf_user` and `buf_api_token`. This will add your auth credentials to the `.netrc` and allow you to access the BSR from anything in your `PATH`.
If you are using Private [Remote Packages](https://docs.buf.build/bsr/remote-packages/overview) you may need to authenticate the entire system to successfully communicate with the [Buf Schema Registry][bsr]. To achieve this, supply both `buf_user` and `buf_api_token`. This will add your auth credentials to the `.netrc` and allow you to access the BSR from anything in your `PATH`.

```yaml
steps:
Expand All @@ -126,16 +128,15 @@ env:
BUF_TOKEN: ${{ secrets.BUF_TOKEN }}
```

Note that this only authenticate you with the `buf` cli. You cannot access your private remote
Note that this only authenticate you with the `buf` cli. You cannot access your private remote
packages in BSR. If you need to access your private remote packages, supply the username and Buf
API Token [as parameters](#buf-username-and-buf-api-token).
API Token [as parameters](#buf-username-and-buf-api-token).

#### Buf domain

If you are working with a private BSR then you can set the `buf_domain` input to the domain of
your instance. Please ensure that you are using a token created on your instance (e.g. `https://buf.example.com/settings/user`) and not from the public BSR at `https://buf.build`.


#### Installing `protoc`

In most cases, you _don't_ need to install [`protoc`][protoc] for Buf's GitHub Actions, but some
Expand Down Expand Up @@ -169,7 +170,6 @@ steps:
```

[action]: https://docs.github.com/actions
[breaking]: https://docs.buf.build/breaking
[bsr]: https://docs.buf.build/bsr
[buf-breaking]: https://github.com/marketplace/actions/buf-breaking
[buf-cli]: https://github.com/bufbuild/buf
Expand Down
157 changes: 157 additions & 0 deletions __tests__/buf-setup.test.ts
@@ -0,0 +1,157 @@
import * as core from "@actions/core";
import * as io from "@actions/io";
import fs from "fs";
import cp from "child_process";
import osm, { type } from "os";
import path from "path";
import * as run from "../src/run";
import * as buf from "../src/buf";

const win32Join = path.win32.join;
const posixJoin = path.posix.join;

//jest.setTimeout(10000);

describe("buf-setup", () => {
let inputs = {} as any;
let os = {} as any;

let inSpy: jest.SpyInstance;
let platSpy: jest.SpyInstance;
let archSpy: jest.SpyInstance;
let joinSpy: jest.SpyInstance;
let execSpy: jest.SpyInstance;
let logSpy: jest.SpyInstance;
let dbgSpy: jest.SpyInstance;
let warnSpy: jest.SpyInstance;
let existsSpy: jest.SpyInstance;
let readFileSpy: jest.SpyInstance;
let getSpy: jest.SpyInstance;
let whichSpy: jest.SpyInstance;
let cnSpy: jest.SpyInstance;
let failedSpy: jest.SpyInstance;

beforeAll(async () => {
process.env["GITHUB_ENV"] = ""; // Stub out Environment file functionality so we can verify it writes to standard out (toolkit is backwards compatible)
}, 100000);

beforeEach(() => {
process.env["GITHUB_PATH"] = ""; // Stub out ENV file functionality so we can verify it writes to standard out

// @actions/core
inputs = {};
// Defaults as per action.yml
inputs["version"] = "1.26.0";
inputs["buf_domain"] = "buf.build";
inSpy = jest.spyOn(core, "getInput");
inSpy.mockImplementation((name) => inputs[name] ?? "");
failedSpy = jest.spyOn(core, "setFailed");

// buf
getSpy = jest.spyOn(buf, "getBuf");
getSpy.mockImplementation(
async (): Promise<string | Error> => "/usr/local"
);

// os
os = {};
platSpy = jest.spyOn(osm, "platform");
platSpy.mockImplementation(() => os["platform"]);
archSpy = jest.spyOn(osm, "arch");

// cp
archSpy.mockImplementation(() => os["arch"]);
execSpy = jest.spyOn(cp, "execSync");

// switch path join behaviour based on set os.platform
joinSpy = jest.spyOn(path, "join");
joinSpy.mockImplementation((...paths: string[]): string => {
if (os["platform"] == "win32") {
return win32Join(...paths);
}

return posixJoin(...paths);
});

// fs
existsSpy = jest.spyOn(fs, "existsSync");
readFileSpy = jest.spyOn(fs, "readFileSync");

// io
whichSpy = jest.spyOn(io, "which");

// writes
cnSpy = jest.spyOn(process.stdout, "write");
logSpy = jest.spyOn(core, "info");
dbgSpy = jest.spyOn(core, "debug");
warnSpy = jest.spyOn(core, "warning");
});

afterEach(() => {
jest.clearAllMocks();
process.exitCode = 0; // Prevent non-zero exit code from failing test.
});

afterAll(async () => {
jest.restoreAllMocks();
}, 100000);

describe("go-version-file", () => {
const goModContents = `module example.com/mymodule
go 1.14
require (
example.com/othermodule v1.2.3
example.com/thismodule v1.2.3
github.com/bufbuild/buf v1.26.1
)
replace example.com/thatmodule => ../thatmodule
exclude example.com/thismodule v1.3.0
`;

it("return version if go_version_file isn't set", async () => {
existsSpy.mockImplementation(() => false);

await run.run();

expect(logSpy).toHaveBeenCalledWith(
`Setting up buf version "${inputs["version"]}"`
);
expect(logSpy).toHaveBeenCalledWith(
`Successfully setup buf version ${inputs["version"]}`
);
});

it("reads version from go_version_file if set", async () => {
inputs["go_version_file"] = "go.mod";
existsSpy.mockImplementation(() => true);
readFileSpy.mockImplementation(() => Buffer.from(goModContents));

await run.run();

expect(warnSpy).toHaveBeenCalledWith(
"Both version and go_version_file inputs are specified, go_version_file will be preferred"
);
expect(logSpy).toHaveBeenCalledWith('Setting up buf version "1.26.1"');
expect(logSpy).toHaveBeenCalledWith(
"Successfully setup buf version 1.26.1"
);
});

it("reports failure if go_version_file doesn't exist", async () => {
inputs["go_version_file"] = "go.mod";
existsSpy.mockImplementation(() => false);

await run.run();

expect(warnSpy).toHaveBeenCalledWith(
"Both version and go_version_file inputs are specified, go_version_file will be preferred"
);
expect(cnSpy).toHaveBeenCalledWith(
`::error::The specified go version file: "go.mod" does not exist${osm.EOL}`
);
});
});
});
3 changes: 3 additions & 0 deletions action.yml
Expand Up @@ -22,6 +22,9 @@ inputs:
description: The domain of the Buf Schema Registry to login to.
required: false
default: 'buf.build'
go_version_file:
description: The go.mod file to read the buf version from.
required: false
runs:
using: "node16"
main: "./dist/main.js"

0 comments on commit ad16c02

Please sign in to comment.