mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 16:54:58 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a60b613ff | ||
|
|
5862725bab | ||
|
|
8660826b05 | ||
|
|
449df91e33 | ||
|
|
ac59173b30 | ||
|
|
fb75e560fc | ||
|
|
69a064e3ea |
93
.github/workflows/container.yaml
vendored
93
.github/workflows/container.yaml
vendored
@@ -40,22 +40,11 @@ jobs:
|
||||
- name: SHA
|
||||
id: sha
|
||||
run: echo "sha=$(/usr/bin/git log -1 --format='%H')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to quay.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ secrets.QUAY_USER }}
|
||||
password: ${{ secrets.QUAY_TOKEN }}
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Fetch tags
|
||||
run: git fetch --prune --unshallow --tags
|
||||
- name: Set Tags
|
||||
@@ -63,8 +52,14 @@ jobs:
|
||||
run: |
|
||||
echo "detail=$(/usr/bin/git describe --tags HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "suffix=$(test -n "$(git status --porcelain)" && echo '-dirty' || echo '')" >> $GITHUB_OUTPUT
|
||||
- name: Setup Cosign to sign container images
|
||||
uses: sigstore/cosign-installer@v3.7.0
|
||||
echo "tag=$(/usr/bin/git describe --tags HEAD)$(test -n "$(git status --porcelain)" && echo '-dirty' || echo '')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push container images
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@v6
|
||||
@@ -73,18 +68,76 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/holos-run/holos:${{ steps.tags.outputs.detail }}${{ steps.tags.outputs.suffix }}
|
||||
ghcr.io/holos-run/holos:${{ steps.tags.outputs.tag }}
|
||||
ghcr.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}
|
||||
quay.io/holos-run/holos:${{ steps.tags.outputs.detail }}${{ steps.tags.outputs.suffix }}
|
||||
quay.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}
|
||||
- name: Setup Cosign to sign container images
|
||||
uses: sigstore/cosign-installer@v3.7.0
|
||||
- name: Sign with GitHub OIDC Token
|
||||
env:
|
||||
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||
run: |
|
||||
cosign sign --yes ghcr.io/holos-run/holos:${{ steps.tags.outputs.detail }}${{ steps.tags.outputs.suffix }}@${DIGEST}
|
||||
cosign sign --yes ghcr.io/holos-run/holos:${{ steps.tags.outputs.tag }}@${DIGEST}
|
||||
cosign sign --yes ghcr.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}@${DIGEST}
|
||||
cosign sign --yes quay.io/holos-run/holos:${{ steps.tags.outputs.detail }}${{ steps.tags.outputs.suffix }}@${DIGEST}
|
||||
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
with:
|
||||
owner: ${{ github.repository_owner }}
|
||||
app-id: ${{ vars.GORELEASER_APP_ID }}
|
||||
private-key: ${{ secrets.GORELEASER_APP_PRIVATE_KEY }}
|
||||
- name: Get GitHub App User ID
|
||||
id: get-user-id
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
- run: |
|
||||
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
|
||||
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com'
|
||||
- name: Update holos-run/holos-action
|
||||
env:
|
||||
IMAGE: ghcr.io/holos-run/holos:v0.102.1
|
||||
VERSION: ${{ steps.tags.outputs.tag }}
|
||||
USER_ID: ${{ steps.get-user-id.outputs.user-id }}
|
||||
TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git clone "https://github.com/holos-run/holos-action"
|
||||
cd holos-action
|
||||
git remote set-url origin https://${USER_ID}:${TOKEN}@github.com/holos-run/holos-action
|
||||
docker pull --quiet "${IMAGE}"
|
||||
docker run -v $(pwd):/app --workdir /app --rm "${IMAGE}" \
|
||||
holos cue export --out yaml action.cue -t "version=${VERSION}" > action.yml
|
||||
git add action.yml
|
||||
git commit -m "ci: update holos to ${VERSION} - https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" || (echo "No changes to commit"; exit 0)
|
||||
git push origin HEAD:main HEAD:v0 HEAD:v1
|
||||
|
||||
- name: Login to quay.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ secrets.QUAY_USER }}
|
||||
password: ${{ secrets.QUAY_TOKEN }}
|
||||
- name: Push to quay.io
|
||||
env:
|
||||
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||
run: |
|
||||
# docker push quay.io/holos-run/holos:${{ steps.tags.outputs.tag }}
|
||||
docker pull --quiet ghcr.io/holos-run/holos:${{ steps.tags.outputs.tag }}@${DIGEST}
|
||||
docker tag ghcr.io/holos-run/holos:${{ steps.tags.outputs.tag }}@${DIGEST} \
|
||||
quay.io/holos-run/holos:${{ steps.tags.outputs.tag }}
|
||||
docker push quay.io/holos-run/holos:${{ steps.tags.outputs.tag }}
|
||||
|
||||
docker pull --quiet ghcr.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}@${DIGEST}
|
||||
docker tag ghcr.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}@${DIGEST} \
|
||||
quay.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}
|
||||
docker push quay.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}
|
||||
- name: Sign quay.io image
|
||||
env:
|
||||
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||
run: |
|
||||
cosign sign --yes quay.io/holos-run/holos:${{ steps.tags.outputs.tag }}@${DIGEST}
|
||||
cosign sign --yes quay.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}@${DIGEST}
|
||||
|
||||
outputs:
|
||||
sha: ${{ steps.sha.outputs.sha }}
|
||||
tag: ${{ steps.tags.outputs.tag }}
|
||||
detail: ${{ steps.tags.outputs.detail }}
|
||||
|
||||
@@ -263,7 +263,7 @@ type Metadata struct {
|
||||
// Labels represents a resource selector.
|
||||
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||
// Annotations represents arbitrary non-identifying metadata. For example
|
||||
// holos uses the `cli.holos.run/description` annotation to log resources in a
|
||||
// holos uses the `app.holos.run/description` annotation to log resources in a
|
||||
// user customized way.
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
}
|
||||
@@ -320,7 +320,7 @@ type Component struct {
|
||||
// resulting BuildPlan.
|
||||
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||
// Annotations represents arbitrary non-identifying metadata. Use the
|
||||
// `cli.holos.run/description` to customize the log message of each BuildPlan.
|
||||
// `app.holos.run/description` to customize the log message of each BuildPlan.
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ type Component struct {
|
||||
// resulting BuildPlan.
|
||||
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||
// Annotations represents arbitrary non-identifying metadata. Use the
|
||||
// `cli.holos.run/description` to customize the log message of each BuildPlan.
|
||||
// `app.holos.run/description` to customize the log message of each BuildPlan.
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
}
|
||||
```
|
||||
@@ -376,7 +376,7 @@ type Metadata struct {
|
||||
// Labels represents a resource selector.
|
||||
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||
// Annotations represents arbitrary non-identifying metadata. For example
|
||||
// holos uses the `cli.holos.run/description` annotation to log resources in a
|
||||
// holos uses the `app.holos.run/description` annotation to log resources in a
|
||||
// user customized way.
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
@@ -19,11 +20,15 @@ import (
|
||||
"github.com/holos-run/holos/internal/util"
|
||||
)
|
||||
|
||||
// cue context and loading is not safe for concurrent use.
|
||||
var cueMutex sync.Mutex
|
||||
|
||||
// ExtractYAML extracts yaml encoded data from file paths. The data is unified
|
||||
// into one [cue.Value]. If a path element is a directory, all files in the
|
||||
// directory are loaded non-recursively.
|
||||
//
|
||||
// Attribution: https://github.com/cue-lang/cue/issues/3504
|
||||
// Deprecated: Use cue embed instead.
|
||||
func ExtractYAML(ctxt *cue.Context, filepaths []string) (cue.Value, error) {
|
||||
value := ctxt.CompileString("")
|
||||
files := make([]string, 0, 10*len(filepaths))
|
||||
@@ -67,6 +72,8 @@ func ExtractYAML(ctxt *cue.Context, filepaths []string) (cue.Value, error) {
|
||||
// extracted data values are unified with the platform configuration [cue.Value]
|
||||
// in the returned [Instance].
|
||||
func LoadInstance(path string, filepaths []string, tags []string) (*Instance, error) {
|
||||
cueMutex.Lock()
|
||||
defer cueMutex.Unlock()
|
||||
root, leaf, err := util.FindRootLeaf(path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
@@ -89,7 +96,6 @@ func LoadInstance(path string, filepaths []string, tags []string) (*Instance, er
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
// TODO: https://cuelang.org/docs/howto/place-data-go-api/
|
||||
value = value.Unify(values[0])
|
||||
|
||||
inst := &Instance{
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
// platform.
|
||||
type PlatformOpts struct {
|
||||
Fn func(context.Context, int, holos.Component) error
|
||||
Selector holos.Selector
|
||||
Selectors holos.Selectors
|
||||
Concurrency int
|
||||
InfoEnabled bool
|
||||
}
|
||||
@@ -31,7 +31,7 @@ type Platform struct {
|
||||
func (p *Platform) Build(ctx context.Context, opts PlatformOpts) error {
|
||||
limit := max(opts.Concurrency, 1)
|
||||
parentStart := time.Now()
|
||||
components := p.Select(opts.Selector)
|
||||
components := p.Select(opts.Selectors...)
|
||||
total := len(components)
|
||||
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
@@ -45,8 +45,8 @@ func newPlatform(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
|
||||
cmd.Flags().StringVar(&platform, "platform", "./platform", "platform directory path")
|
||||
var extractYAMLs holos.StringSlice
|
||||
cmd.Flags().Var(&extractYAMLs, "extract-yaml", "data file paths to extract and unify with the platform config")
|
||||
var selector holos.Selector
|
||||
cmd.Flags().VarP(&selector, "selector", "l", "label selector (e.g. label==string,label!=string)")
|
||||
var selectors holos.Selectors
|
||||
cmd.Flags().VarP(&selectors, "selector", "l", "label selector (e.g. label==string,label!=string)")
|
||||
tagMap := make(holos.TagMap)
|
||||
cmd.Flags().VarP(&tagMap, "inject", "t", tagHelp)
|
||||
|
||||
@@ -75,7 +75,7 @@ func newPlatform(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
|
||||
}
|
||||
opts := builder.PlatformOpts{
|
||||
Fn: makeComponentRenderFunc(cmd.ErrOrStderr(), prefixArgs, tagMap.Tags()),
|
||||
Selector: selector,
|
||||
Selectors: selectors,
|
||||
Concurrency: concurrency,
|
||||
InfoEnabled: true,
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ func newShowBuildPlanCmd() (cmd *cobra.Command) {
|
||||
cmd.Flags().Var(&extractYAMLs, "extract-yaml", "data file paths to extract and unify with the platform config")
|
||||
var format string
|
||||
cmd.Flags().StringVar(&format, "format", "yaml", "yaml or json format")
|
||||
var selector holos.Selector
|
||||
cmd.Flags().VarP(&selector, "selector", "l", "label selector (e.g. label==string,label!=string)")
|
||||
var selectors holos.Selectors
|
||||
cmd.Flags().VarP(&selectors, "selector", "l", "label selector (e.g. label==string,label!=string)")
|
||||
tagMap := make(holos.TagMap)
|
||||
cmd.Flags().VarP(&tagMap, "inject", "t", "set the value of a cue @tag field from a key=value pair")
|
||||
var concurrency int
|
||||
@@ -102,7 +102,7 @@ func newShowBuildPlanCmd() (cmd *cobra.Command) {
|
||||
|
||||
platformOpts := builder.PlatformOpts{
|
||||
Fn: makeBuildFunc(encoder, buildPlanOpts),
|
||||
Selector: selector,
|
||||
Selectors: selectors,
|
||||
Concurrency: concurrency,
|
||||
}
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ package core
|
||||
labels?: {[string]: string} @go(Labels,map[string]string)
|
||||
|
||||
// Annotations represents arbitrary non-identifying metadata. For example
|
||||
// holos uses the `cli.holos.run/description` annotation to log resources in a
|
||||
// holos uses the `app.holos.run/description` annotation to log resources in a
|
||||
// user customized way.
|
||||
annotations?: {[string]: string} @go(Annotations,map[string]string)
|
||||
}
|
||||
@@ -355,7 +355,7 @@ package core
|
||||
labels?: {[string]: string} @go(Labels,map[string]string)
|
||||
|
||||
// Annotations represents arbitrary non-identifying metadata. Use the
|
||||
// `cli.holos.run/description` to customize the log message of each BuildPlan.
|
||||
// `app.holos.run/description` to customize the log message of each BuildPlan.
|
||||
annotations?: {[string]: string} @go(Annotations,map[string]string)
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,28 @@ func (e *EnvFlagger) Flag(name feature) bool {
|
||||
|
||||
type Labels map[string]string
|
||||
|
||||
type Selectors []Selector
|
||||
|
||||
// String implements the flag.Value interface.
|
||||
func (s *Selectors) String() string {
|
||||
return fmt.Sprint(*s)
|
||||
}
|
||||
|
||||
// Type implements the pflag.Value interface and describes the type.
|
||||
func (s *Selectors) Type() string {
|
||||
return "selectors"
|
||||
}
|
||||
|
||||
// Set implements the flag.Value interface.
|
||||
func (s *Selectors) Set(value string) error {
|
||||
selector := Selector{}
|
||||
if err := selector.Set(value); err != nil {
|
||||
return err
|
||||
}
|
||||
*s = append(*s, selector)
|
||||
return nil
|
||||
}
|
||||
|
||||
type Selector struct {
|
||||
Positive map[string]string
|
||||
Negative map[string]string
|
||||
@@ -118,14 +140,9 @@ func (s *Selector) IsSelected(labels Labels) bool {
|
||||
return true // Nil selector selects everything
|
||||
}
|
||||
|
||||
if len(s.Positive) == 0 && len(s.Negative) == 0 {
|
||||
return true // Empty selector selects everything
|
||||
}
|
||||
|
||||
// Check positive matches
|
||||
for k, v := range s.Positive {
|
||||
val, ok := labels[k]
|
||||
if !ok || v != val {
|
||||
if val, ok := labels[k]; !ok || v != val {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -251,15 +268,18 @@ func (y *yamlEncoder) Close() error {
|
||||
return errors.Wrap(y.enc.Close())
|
||||
}
|
||||
|
||||
// IsSelected returns true if all selectors select the given labels or no
|
||||
// IsSelected returns true if any one selector selects the given labels or no
|
||||
// selectors are given.
|
||||
func IsSelected(labels Labels, selectors ...Selector) bool {
|
||||
if len(selectors) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, selector := range selectors {
|
||||
if !selector.IsSelected(labels) {
|
||||
return false
|
||||
if selector.IsSelected(labels) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
type orderedEncoder struct {
|
||||
|
||||
@@ -1 +1 @@
|
||||
1
|
||||
4
|
||||
|
||||
Reference in New Issue
Block a user