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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
run
/RUN
/INSTALL
12 changes: 8 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
FROM golang:alpine AS builder
RUN apk add --no-cache git ca-certificates
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY main.go ./
COPY hack/ca-certificates.crt ./hack/
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o RUN . && \
(apk add --no-cache upx && upx --best --lzma RUN || true)
COPY . ./
# Copy Alpine's CA certificates to the location our code expects
RUN cp /etc/ssl/certs/ca-certificates.crt internal/certs/ca-certificates.crt
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o RUN ./cmd/run && \
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o INSTALL ./cmd/install && \
(apk add --no-cache upx && upx --best --lzma RUN INSTALL || true)

FROM scratch
COPY --from=builder /build/RUN /usr/local/bin/RUN
COPY --from=builder /build/INSTALL /usr/local/bin/INSTALL
ENTRYPOINT ["RUN"]
CMD ["--help"]
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
# RUN
# RUN & INSTALL

A minimal Docker image that fetches and executes shell scripts from URLs. Uses [mvdan/sh](https://github.com/mvdan/sh), a POSIX shell interpreter written in pure Go.
Minimal Docker image that fetches and executes shell scripts from URLs. Uses [mvdan/sh](https://github.com/mvdan/sh), a POSIX shell interpreter written in pure Go.

Includes two commands:
- **RUN**: Fetch and execute scripts (for runtime execution)
- **INSTALL**: Coming soon (for installation/setup tasks)

## Usage

```dockerfile
FROM scaffoldly/run AS run
FROM installable/sh AS run

FROM ubuntu:latest
COPY --from=run / /
CMD ["RUN", "https://example.com/script.sh", "arg1", "arg2"]
```

The `COPY --from=run / /` pattern adds the `RUN` binary to any base image, allowing scripts to use the base image's utilities.
The `COPY --from=run / /` pattern adds the `RUN` and `INSTALL` binaries to any base image, allowing scripts to use the base image's utilities.

Also available at `ghcr.io/scaffoldly/run`.
Published on Docker Hub as `installable/sh`.

See the [examples](./examples) directory for more examples.

Expand All @@ -34,7 +38,7 @@ This allows dynamic script generation based on the container's environment.

## Custom User-Agent

The default User-Agent is `run/1.0 (scaffoldly)`. Set the `USER_AGENT` environment variable to override it:
The default User-Agent is `run/1.0 (installable)`. Set the `USER_AGENT` environment variable to override it:

```dockerfile
ENV USER_AGENT="MyApp/1.0"
Expand Down Expand Up @@ -76,9 +80,11 @@ This sets `Cache-Control: no-cache, no-store, must-revalidate` and `Pragma: no-c
## Building

```bash
docker build -t run .
docker build -t installable/sh .
```

**Note**: The Dockerfile copies CA certificates from Alpine's `ca-certificates` package during the build. The certificates in `internal/certs/` and `hack/` directories are placeholders for local development only.

## Limitations

- Scripts must be POSIX-compliant (no bash-specific features like arrays, `[[`, `set -E`, etc.)
Expand Down
11 changes: 11 additions & 0 deletions cmd/install/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package main

import (
"fmt"
"os"
)

func main() {
fmt.Fprintln(os.Stderr, "INSTALL: coming soon")
os.Exit(1)
}
9 changes: 3 additions & 6 deletions main.go → cmd/run/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"context"
"crypto/tls"
"crypto/x509"
_ "embed"
"fmt"
"io"
"mime"
Expand All @@ -16,14 +15,12 @@ import (
"strings"

"github.com/hashicorp/go-retryablehttp"
"github.com/installable-sh/docker/internal/certs"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
)

//go:embed hack/ca-certificates.crt
var caCerts []byte

type parsedArgs struct {
showHelp bool
sendEnv bool
Expand Down Expand Up @@ -129,7 +126,7 @@ func fetchScript(client *retryablehttp.Client, args parsedArgs) (fetchedScript,
return fetchedScript{}, err
}

userAgent := "run/1.0 (scaffoldly)"
userAgent := "run/1.0 (installable)"
if ua := os.Getenv("USER_AGENT"); ua != "" {
userAgent = ua
}
Expand Down Expand Up @@ -200,7 +197,7 @@ func fetchScript(client *retryablehttp.Client, args parsedArgs) (fetchedScript,

func newHTTPClient() (*retryablehttp.Client, error) {
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(caCerts) {
if !certPool.AppendCertsFromPEM(certs.CACerts) {
return nil, fmt.Errorf("failed to parse embedded CA certificates")
}

Expand Down
4 changes: 2 additions & 2 deletions examples/claude-code/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Import the RUN image
FROM scaffoldly/run AS run
# Import the installable/sh image
FROM installable/sh AS run

# Install Claude Code using the RUN binary
FROM ubuntu:latest AS claude
Expand Down
6 changes: 3 additions & 3 deletions examples/hello-world/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Import the RUN image
FROM scaffoldly/run AS run
FROM installable/sh AS run

# Use any base image with the utilities your script needs
FROM ubuntu:latest

# Add the RUN binary to the image
# Add the RUN and INSTALL binaries to the image
COPY --from=run / /

# Fetch and execute a shell script from a URL
CMD ["RUN", "https://raw.githubusercontent.com/scaffoldly/run/main/examples/hello-world/hello-world.sh"]
CMD ["RUN", "https://raw.githubusercontent.com/installable-sh/docker/main/examples/hello-world/hello-world.sh"]
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/scaffoldly/run
module github.com/installable-sh/docker

go 1.25.4

Expand Down
12 changes: 8 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
Expand Down
Loading