Skip to content
Draft
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
11 changes: 5 additions & 6 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
MISE_GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
MISE_VERSION: "v2025.12.0" # NOTE: set via 'make set-mise-version'

jobs:
ci:
Expand Down Expand Up @@ -42,10 +43,11 @@ jobs:
make
- name: "Set up mise"
uses: "jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566" # v3.2.0
with:
version: "2025.9.10"
with: &mise_with
version: "${{ env.MISE_VERSION }}"
install: true # runs `mise install`
cache: true
github_token: "${{ secrets.GITHUB_TOKEN }}"
- name: "Run CI Tasks"
run: "make ci"
deliver:
Expand Down Expand Up @@ -73,10 +75,7 @@ jobs:
make
- name: "Set up mise"
uses: "jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566" # v3.2.0
with:
version: "2025.9.10"
install: true # runs `mise install`
cache: true
with: *mise_with
- name: "Set up Docker"
uses: "docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435" # v3.11.1
- name: "Run Delivery Tasks"
Expand Down
11 changes: 9 additions & 2 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
ARG GITHUB_TOKEN
ARG GO_VERSION
ARG GOPRIVATE
ARG MISE_GITHUB_TOKEN
ARG MISE_VERSION

# These two proxy args are helpful if you're trying to build on a corporate network -- they do not
Expand All @@ -14,13 +17,15 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
bash \
ca-certificates \
make \
upx \
&& \
rm -rf /var/lib/apt*

COPY . /go/app
WORKDIR /go/app

RUN make build
RUN --mount=type=cache,target=/go/pkg/mod \
make build

####################################################################################################

Expand All @@ -44,7 +49,9 @@ WORKDIR /go/app

# NOTE: when creating some shims, mise refers to itself assuming it is on the $PATH, so we need to
# symlink it out so it can do that
RUN ln -fs "${HOME}/.oscar/bin/mise" /usr/local/bin/mise && \
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.oscar \
ln -fs "${HOME}/.oscar/bin/mise" /usr/local/bin/mise && \
bash ./scripts/test-bootstrap.sh setup && \
/oscar ci

Expand Down
20 changes: 17 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ export IMAGE_NAME ?= $(BINNAME)
export IMAGE_TAG ?= latest
export IMAGE_URI ?= $(IMAGE_REGISTRY)/$(IMAGE_REGISTRY_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)

SHELL = /usr/bin/env bash -euo pipefail
# use the intended GNU tools on macOS
gawk = $(shell command -v gawk)
gsed = $(shell command -v sed)
ifeq ($(shell uname -s),Darwin)
gawk = $(shell command -v gawk)
gsed = $(shell command -v gsed)
endif

all: ci

Expand All @@ -36,7 +42,9 @@ test: ci
# have a stage-copiable output
build: FORCE
@$(RUN) go build -ldflags '-s -w -extldflags "-static"' -o ./build/$(BINNAME) ./cmd/$(BINNAME)
@upx --best ./build/$(BINNAME)
@if [[ "$$(uname -s)" == "Linux" ]] ; then \
upx --best ./build/$(BINNAME) ; \
fi

clean: FORCE
@rm -rf \
Expand All @@ -51,11 +59,17 @@ clean: FORCE

image: clean
@export BUILDKIT_PROGRESS=plain && \
export GO_VERSION="$$(awk '/^go/ { print $$2 }' go.mod)" && \
export GO_VERSION="$$($(gawk) '/^go/ { print $$2 }' go.mod)" && \
$(RUN) $(DOCKER) compose build

run-image: FORCE
@$(RUN) $(DOCKER) compose run $(BINNAME)

generate: FORCE
@cd ./proto && $(RUN) buf generate

# Since some variant of "MISE_VERSION" shows up in a lot of disjointed places in the codebase, this
# lets us update all of them at once
set-mise-version:
@find . -type f \
| xargs -I{} $(gsed) -i -E 's/(MISE_VERSION.*|MiseVersion.*)([0-9]{4}\.[0-9]+\.[0-9]+)/\1$(mise_version)/g' {}
10 changes: 8 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@ services:
context: "."
dockerfile: "./Containerfile"
args:
GO_VERSION: "${GO_VERSION:-1.25.0}"
MISE_VERSION: "${MISE_VERSION:-v2025.9.10}"
GITHUB_TOKEN: "${GITHUB_TOKEN:-}"
GO_VERSION: "${GO_VERSION:-}"
GOPRIVATE: "${GOPRIVATE:-}"
http_proxy: "${http_proxy:-}"
https_proxy: "${https_proxy:-}"
MISE_GITHUB_TOKEN: "${MISE_GITHUB_TOKEN:-}"
MISE_VERSION: "${MISE_VERSION:-v2025.12.0}" # NOTE: set via 'make set-mise-version'
image: "${IMAGE_URI:-}"
pull_policy: "build"
command:
- "ci"
environment:
http_proxy: "${http_proxy}"
https_proxy: "${https_proxy}"
GITHUB_TOKEN: "${GITHUB_TOKEN}"
MISE_GITHUB_TOKEN: "${MISE_GITHUB_TOKEN}"
GOPRIVATE: "${GOPRIVATE}"
volumes:
- type: "bind"
source: "${PWD}"
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/opensourcecorp/oscar

go 1.24.4
go 1.25.5

require (
github.com/urfave/cli/v3 v3.4.1
Expand All @@ -11,7 +11,7 @@ require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.9-20250912141014-52f32327d4b0.1
buf.build/go/protovalidate v1.0.0
github.com/stretchr/testify v1.11.1
go.yaml.in/yaml/v4 v4.0.0-rc.2
go.yaml.in/yaml/v4 v4.0.0-rc.3
golang.org/x/term v0.35.0
google.golang.org/protobuf v1.36.9
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/urfave/cli/v3 v3.4.1 h1:1M9UOCy5bLmGnuu1yn3t3CB4rG79Rtoxuv1sPhnm6qM=
github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s=
go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go=
go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
Expand Down
15 changes: 10 additions & 5 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
)

const (
// Command names and their flags
// Command names and their flags.
rootCmdName = "oscar"
debugFlagName = "debug"
noBannerFlagName = "no-banner"
Expand Down Expand Up @@ -57,8 +57,9 @@ func NewRootCmd() *cli.Command {
},
},
&cli.BoolFlag{
Name: noColorFlagName,
Usage: "Pass to suppress printing colored terminal output. Note that oscar defaults to printing in color during interactive runs.",
Name: noColorFlagName,
Usage: "Pass to suppress printing colored terminal output. " +
"Note that oscar defaults to printing in color during interactive runs.",
Sources: cli.EnvVars(consts.OscarEnvVarNoColor),
Action: func(_ context.Context, _ *cli.Command, _ bool) error {
return os.Setenv(consts.OscarEnvVarNoColor, "true")
Expand Down Expand Up @@ -89,13 +90,17 @@ func getVersion() (string, error) {
return "", fmt.Errorf("reading oscar config file: %w", err)
}

return cfg.Version, nil
return cfg.GetVersion(), nil
}

// rootAction defines the logic for oscar's root command.
func rootAction(_ context.Context, cmd *cli.Command) error {
iprint.Debugf("oscar root command\n")
_ = cli.ShowAppHelp(cmd)

if err := cli.ShowAppHelp(cmd); err != nil {
return fmt.Errorf("internal error trying to show command help: %w", err)
}

return errors.New("\nERROR: oscar requires a valid subcommand")
}

Expand Down
6 changes: 4 additions & 2 deletions internal/consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const (

// MiseVersion is the default version of mise to install if not present. Can be overridden via
// the `MISE_VERSION` env var, which is checked elsewhere.
MiseVersion = "v2025.9.10"
//
// NOTE: set via 'make set-mise-version'.
MiseVersion = "v2025.12.0"

// DefaultOscarCfgFileName is the default basename of oscar's config file.
DefaultOscarCfgFileName = "oscar.yaml"
Expand All @@ -34,7 +36,7 @@ var (
// MiseBinPath is the absolute path to the mise binary, if oscar is the one installing it.
MiseBinPath = filepath.Join(OscarHomeBin, "mise")

// MiseConfigFileName is the basename of the mise configuration file that oscar uses
// MiseConfigFileName is the basename of the mise configuration file that oscar uses.
MiseConfigFileName = "mise.oscar.toml"

// MiseEnvVars maps mise's env var keys to their desired values.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 39 additions & 29 deletions internal/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,58 @@ import (
"github.com/opensourcecorp/oscar/internal/system"
)

// Git holds metadata about the current state of the Git repository.
type Git struct {
// The root directory of the repository on the host.
Root string
// The current branch name.
Branch string
// The latest tag available in the repo.
LatestTag string
// The latest commit on the current branch.
LatestCommit string
// Whether or not the working directory has uncommitted changes.
IsDirty bool
}
type (
// Git holds metadata about the current state of the Git repository.
Git struct {
// The root directory of the repository on the host.
Root string
// The current branch name.
Branch string
// The latest tag available in the repo.
LatestTag string
// The latest commit on the current branch.
LatestCommit string
// Whether or not the working directory has uncommitted changes.
IsDirty bool
}

// Status holds various pieces of information about Git status.
type Status struct {
// The list of modified files.
Diff []string
// The list of untracked files.
UntrackedFiles []string
}
// Status holds various pieces of information about Git status.
Status struct {
// The list of modified files.
Diff []string
// The list of untracked files.
UntrackedFiles []string
}
)

// New returns a populated [Git].
func New(ctx context.Context) (*Git, error) {
root, err := system.RunCommand(ctx, []string{"git", "rev-parse", "--show-toplevel"})
if err != nil {
return nil, err
return nil, fmt.Errorf("getting Git root: %w", err)
}

iprint.Debugf("Git root on host: '%s'\n", root)

branch, err := system.RunCommand(ctx, []string{"git", "rev-parse", "--abbrev-ref", "HEAD"})
if err != nil {
return nil, err
return nil, fmt.Errorf("getting Git branch name: %w", err)
}

iprint.Debugf("Git branch: '%s'\n", branch)

latestTag, err := system.RunCommand(ctx, []string{"bash", "-c", "git tag --list | tail -n1"})
if err != nil {
return nil, err
return nil, fmt.Errorf("getting latest Git tag: %w", err)
}

iprint.Debugf("latest Git tag: '%s'\n", latestTag)

latestCommit, err := system.RunCommand(ctx, []string{"git", "rev-parse", "--short=8", "HEAD"})
if err != nil {
return nil, err
return nil, fmt.Errorf("getting latest Git commit: %w", err)
}

iprint.Debugf("latest Git commit: '%s'\n", latestCommit)

gitStatus, err := getRawStatus(ctx)
Expand Down Expand Up @@ -89,19 +95,21 @@ func (g *Git) SanitizedBranch() string {

// String implements [fmt.Stringer].
func (g *Git) String() string {
out := "Current Git information:\n"
var out strings.Builder
out.WriteString("Current Git information:\n")

t := reflect.TypeFor[Git]()

t := reflect.TypeOf(*g)
v := reflect.ValueOf(*g)
for i := range v.NumField() {
field := t.Field(i)
value := v.Field(i)
out += fmt.Sprintf("- %s: %v\n", field.Name, value)
out.WriteString(fmt.Sprintf("- %s: %v\n", field.Name, value))
}

out += "\n"
out.WriteString("\n")

return out
return out.String()
}

// getRawStatus returns a slightly-modified "git status" output, so that calling tools can parse it
Expand All @@ -117,10 +125,12 @@ func getRawStatus(ctx context.Context) (Status, error) {

untrackedFiles := make([]string, 0)
diff := make([]string, 0)

for _, line := range outputSplit {
if line == "" {
continue
}

if strings.HasPrefix(line, "??") {
filename := strings.ReplaceAll(line, "?? ", "")
untrackedFiles = append(untrackedFiles, filename)
Expand Down
Loading