Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 151 additions & 54 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,87 +11,184 @@ defaults:
shell: bash

jobs:
release:
prepare:
runs-on: ubuntu-latest
permissions:
contents: write
env:
VERSION: ""
COMMIT: ""
RELEASE: ""
GH_TOKEN: ${{ github.token }}
outputs:
version: ${{ steps.release.outputs.version }}
release: ${{ steps.release.outputs.release }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: 'stable'
- run: go version

- name: Check need release
id: check_release
id: release
run: |
version=""
release="false"
release=false
new_ver=$(grep -Po "v\d+\.\d+\.\d+" cmd/compiledb/main.go)
cur_ver=$(git describe --abbrev=0 --tags)
commit=$(git rev-parse --short HEAD)
if [[ "$new_ver" != "$cur_ver" && $(git branch --show-current) == "main" ]]; then
if [[ "$new_ver" != "$cur_ver" && "$GITHUB_REF" == "refs/heads/main" ]]; then
version=$new_ver
release="true"
release=true
else
version=dev-$new_ver\($commit\)
sed -E "s|v[0-9]+\.[0-9]+\.[0-9]+|$version|" cmd/compiledb/main.go -i
version="dev-${new_ver}(${commit})"
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "release=$release" >> "$GITHUB_OUTPUT"

linux-amd64:
needs: prepare
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable

- name: Check format
run: |
unformatted=$(gofmt -l .)
if [[ -n "$unformatted" ]]; then
printf 'Files need gofmt:\n%s\n' "$unformatted"
exit 1
fi
echo "RELEASE=$release" >> $GITHUB_ENV
echo "VERSION=$version" >> $GITHUB_ENV
echo "COMMIT=$commit" >> $GITHUB_ENV

- name: Install dependencies
run: go mod tidy
- name: Check dependencies
run: |
go mod tidy
git diff --exit-code -- go.mod go.sum

- name: Vet
run: go vet ./...

- name: Test
run: go test ./...

- name: Race test
run: go test -race ./...

- name: Build and smoke test
run: |
test "$(go env GOOS)/$(go env GOARCH)" = "linux/amd64"
mkdir -p dist
go build -ldflags "-X main.Version=$VERSION" -o dist/compiledb ./cmd/compiledb
./dist/compiledb --help
tar cJf dist/compiledb-linux-amd64.txz -C dist compiledb

- uses: actions/upload-artifact@v4
with:
name: release-linux-amd64
path: dist/compiledb-linux-amd64.txz
if-no-files-found: error

linux-arm64:
needs: prepare
runs-on: ubuntu-24.04-arm
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable

- name: Build and smoke test
run: |
test "$(go env GOOS)/$(go env GOARCH)" = "linux/arm64"
mkdir -p dist
go build -ldflags "-X main.Version=$VERSION" -o dist/compiledb ./cmd/compiledb
./dist/compiledb --help
tar cJf dist/compiledb-linux-arm64.txz -C dist compiledb

- uses: actions/upload-artifact@v4
with:
name: release-linux-arm64
path: dist/compiledb-linux-arm64.txz
if-no-files-found: error

- name: Build
windows-amd64:
needs: prepare
runs-on: windows-latest
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable

- name: Build and smoke test
run: |
out=$(pwd)/build
mkdir -p $out
test "$(go env GOOS)/$(go env GOARCH)" = "windows/amd64"
mkdir -p dist
go build -ldflags "-X main.Version=$VERSION" -o dist/compiledb.exe ./cmd/compiledb
./dist/compiledb.exe --help
(cd dist && 7z a compiledb-windows-amd64.zip compiledb.exe)

cd ./cmd/compiledb
- uses: actions/upload-artifact@v4
with:
name: release-windows-amd64
path: dist/compiledb-windows-amd64.zip
if-no-files-found: error

echo "Build linux-amd64 version"
GOOS=linux GOARCH=amd64 go build
chmod +x compiledb
./compiledb -h | head -1 || true
tar cJvf compiledb.txz compiledb
mv compiledb.txz $out/compiledb-linux-amd64.txz
darwin-arm64:
needs: prepare
runs-on: macos-15
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable

echo "Build linux-arm64 version"
GOOS=linux GOARCH=arm64 go build
chmod +x compiledb
tar cJvf compiledb.txz compiledb
mv compiledb.txz $out/compiledb-linux-arm64.txz
- name: Build and smoke test
run: |
test "$(go env GOOS)/$(go env GOARCH)" = "darwin/arm64"
mkdir -p dist
go build -ldflags "-X main.Version=$VERSION" -o dist/compiledb ./cmd/compiledb
./dist/compiledb --help
tar cJf dist/compiledb-darwin-arm64.txz -C dist compiledb

echo "Build windows-amd64 version"
GOOS=windows GOARCH=amd64 go build
7z a compiledb.zip compiledb.exe
mv compiledb.zip $out/compiledb-windows-amd64.zip
- uses: actions/upload-artifact@v4
with:
name: release-darwin-arm64
path: dist/compiledb-darwin-arm64.txz
if-no-files-found: error

echo "Build darwin-arm64 version"
GOOS=darwin GOARCH=arm64 go build
chmod +x compiledb
tar cJvf compiledb.txz compiledb
mv compiledb.txz $out/compiledb-darwin-arm64.txz
release:
needs: [ prepare, linux-amd64, linux-arm64, windows-amd64, darwin-arm64 ]
if: ${{ github.event_name != 'pull_request' }}
runs-on: ubuntu-latest
permissions:
contents: write
env:
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
pattern: release-*
path: build
merge-multiple: true

# delete draft
gh release delete dev --cleanup-tag -y || true
- name: List artifacts
run: ls -la build

ls -la $out
- name: Delete previous development release
run: |
if gh release view dev >/dev/null 2>&1; then
gh release delete dev -y
fi

- name: Release
if: ${{ env.RELEASE == 'true' }}
if: ${{ needs.prepare.outputs.release == 'true' }}
uses: ncipollo/release-action@v1
with:
tag: ${{ env.VERSION }}
tag: ${{ needs.prepare.outputs.version }}
allowUpdates: true
artifactErrorsFailBuild: true
generateReleaseNotes: true
Expand All @@ -103,7 +200,7 @@ jobs:
tag: dev
artifactErrorsFailBuild: true
generateReleaseNotes: true
name: ${{ env.VERSION }}
name: ${{ needs.prepare.outputs.version }}
prerelease: true
draft: true
artifacts: "build/*"
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
- Focus a test with `go test ./internal -run '^TestName$'` or `go test ./cmd/compiledb -run '^TestName$'`.
- Build without leaving a repository artifact using `go build -o /tmp/compiledb-go ./cmd/compiledb`. The root `compiledb` binary is ignored, but `/tmp` is preferred for manual verification.
- Do not treat `just` as a normal build check. `.justfile` requires Nushell, and its default `build` recipe runs the CLI against `tests/build.log`, generating `compile_commands.json` rather than only compiling the binary.
- The release workflow builds artifacts but does not run tests, vet, or race checks. Local verification remains required.
- The release workflow gates Linux amd64 on gofmt, vet, tests, and race tests. Linux amd64/arm64, Windows amd64, and macOS arm64 artifacts are built natively and must run `--help` before publication. Local verification remains required.

## Test And Artifact Gotchas

Expand All @@ -54,6 +54,6 @@

## Release Gotchas

- `Version` in `cmd/compiledb/main.go` is parsed directly by `.github/workflows/release.yml`. On `main`, a version different from the latest tag triggers a tagged release; non-release builds rewrite it to a `dev-...` version during CI.
- `Version` in `cmd/compiledb/main.go` is parsed directly by `.github/workflows/release.yml`. On `main`, a version different from the latest tag triggers a tagged release; CI injects `dev-...` for non-release artifacts through a linker flag.
- Do not change `Version` as part of unrelated work. When changing it intentionally, review the release workflow and tag state together.
- The release workflow runs `go mod tidy` before building. Dependency changes must leave `go.mod` and `go.sum` tidy locally rather than relying on CI to rewrite them.
- The Linux release gate runs `go mod tidy` and rejects changes to `go.mod` or `go.sum`. Dependency changes must leave both files tidy locally rather than relying on CI to rewrite them.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,10 @@ could use it with some great tools, such as:
- [Neovim][neovim] + [LanguageClient-neovim][lsp] + [cquery][cquery] + [deoplete][deoplete]
- [Neovim][neovim] + [ALE][ale] + [ccls][ccls]

Current release automation cross-builds Linux amd64/arm64, Windows amd64, and macOS arm64
artifacts on an Ubuntu runner. Only the Linux amd64 artifact receives a `compiledb -h` runtime
smoke check there; the workflow does not currently run the test suite or native Windows, macOS,
or arm64 runtime tests.
The release workflow supports Linux amd64/arm64, Windows amd64, and macOS arm64. Each artifact is
built on a native GitHub-hosted runner and must successfully execute `compiledb --help` before it
can be published. Linux amd64 additionally gates releases on `gofmt`, `go vet`, the full test suite,
and the race detector. Other platforms are unsupported and receive no release artifacts.

## License
GNU GPLv3
Expand Down
24 changes: 19 additions & 5 deletions internal/make_wrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -687,12 +687,19 @@ func TestMakeWrapPreservesQuotedMakeFlagAssignments(t *testing.T) {
}

func TestMakeWrapDetectsEnvironmentStdinBeforeUnmatchedQuote(t *testing.T) {
makeExecutable, err := exec.LookPath("make")
if err != nil {
t.Skip("GNU Make is not available")
}
t.Setenv("MAKEFLAGS", "-f - FOO='x")
tmpDir := t.TempDir()
makeExecutable := filepath.Join(tmpDir, "fake-make.sh")
contents := `#!/bin/sh
input=$(cat)
case "$input" in
*"cc -c unmatched-quote.c"*) echo 'cc -c unmatched-quote.c' ;;
*) exit 9 ;;
esac
`
if err := os.WriteFile(makeExecutable, []byte(contents), 0o755); err != nil {
t.Fatalf("write fake make failed: %v", err)
}
stdin, err := os.CreateTemp(tmpDir, "Makefile")
if err != nil {
t.Fatalf("create stdin Makefile failed: %v", err)
Expand All @@ -712,7 +719,14 @@ func TestMakeWrapDetectsEnvironmentStdinBeforeUnmatchedQuote(t *testing.T) {
defer func() { makePath = oldMakePath }()

outputFile := filepath.Join(tmpDir, "compile_commands.json")
tool := newTestTool(t, Config{OutputFile: outputFile, RegexCompile: RegexCompile, RegexFile: RegexFile, NoBuild: true, NoStrict: true})
tool := newTestTool(t, Config{
OutputFile: outputFile,
RegexCompile: RegexCompile,
RegexFile: RegexFile,
NoBuild: true,
NoStrict: true,
Encoding: EncodingRaw,
})
tool.MakeWrap(nil)
commands := readCompilerTestCommands(t, outputFile)
if tool.StatusCode != 0 || len(commands) != 1 || commands[0].File != "unmatched-quote.c" {
Expand Down
6 changes: 5 additions & 1 deletion internal/process_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,11 @@ func TestRunMakeCommandBoundsContinuousOutputAfterLeaderExit(t *testing.T) {
tmpDir := t.TempDir()
pidFile := filepath.Join(tmpDir, "continuous-output.pid")
ctx := context.Background()
cmd := processUnixHelperCommand(ctx, "continuous-parent", "COMPILEDB_TEST_PROCESS_PID_FILE="+pidFile)
// The race runtime's default one-second exit delay is unrelated to output draining.
cmd := processUnixHelperCommand(ctx, "continuous-parent",
"COMPILEDB_TEST_PROCESS_PID_FILE="+pidFile,
"GORACE=atexit_sleep_ms=0",
)
configureMakeCommand(cmd, ctx)

type commandResult struct {
Expand Down
Loading
Loading