Compare commits

...
Author SHA1 Message Date
sixsmith 309fc72c38 temp 2026-10-10 01:04:50 -04:00
sixsmith 455b5fef21 revert Makefile change, moved to another CL 2026-10-09 21:58:43 -04:00
sixsmith d8742e728c Merge remote-tracking branch 'toolshed/main' 2026-10-09 21:56:00 -04:00
sixsmith eaec0cc733 update digest pinning work
- formatter.RemoveSha
- make pindigests TRUE by default
- more strict matching of digests
- unit and integration tests
2026-10-09 21:51:51 -04:00
renovate-bot 5b42b3d306 chore(deps): update golang docker tag 2026-10-09 17:01:20 +00:00
renovate-bot d99e86fb0e chore(deps): update module golang.org/x/term to v0.47.0 2026-10-09 16:01:52 +00:00
renovate-bot ade646abbc chore(deps): update golang docker tag 2026-10-09 14:01:30 +00:00
renovate-bot 80b3b90905 chore(deps): update module golang.org/x/sys to v0.49.0 2026-10-09 13:01:52 +00:00
renovate-bot fab3f0b97b chore(deps): update golang docker tag 2026-10-04 16:01:27 +00:00
renovate-bot 3819ee38f5 chore(deps): update module github.com/go-git/go-git/v5 to v5.19.3 2026-10-04 15:02:06 +00:00
renovate-bot 74261a7a01 chore(deps): update otel/weaver docker tag to v0.27.0 2026-10-04 01:01:15 +00:00
sixsmith 0f7d991709 add lint rule for images with digest 2026-09-10 00:13:35 -04:00
sixsmith 39050a85ef run make-i18n 2026-09-10 00:13:35 -04:00
sixsmith 92bab8637d add --pindigests flag
* handle digests for the recipe subcommand
* add make parameter to pass args
2026-09-10 00:11:52 -04:00
sixsmith 7932356746 add regclient to fetch image manifest/digest
* also run `go mod vendor`
2026-09-10 00:11:52 -04:00
sixsmith 91fe901264 add docs and test 2026-09-10 00:11:52 -04:00
353 changed files with 33578 additions and 5601 deletions
+1
View File
@@ -7,3 +7,4 @@
/bin
dist/
tests/integration/.bats
tests/resources/path_to_repo
+50
View File
@@ -84,3 +84,53 @@ build-mo:
release:
@goreleaser release --clean
CLIENT_VM := abra-client
vm-client-status:
sudo systemctl status microvm@$(CLIENT_VM).service
vm-client-create:test-integration-hosts
sudo microvm -f git+file://$$(pwd) -c $(CLIENT_VM)
vm-client-start:
sudo systemctl start microvm@$(CLIENT_VM).service
vm-client-update:test-integration-hosts
sudo microvm -R -f git+file://$$(pwd) -u $(CLIENT_VM)
vm-client-run:
sudo microvm -f git+file://$$(pwd) -r $(CLIENT_VM)
vm-client-stop:
sudo systemctl stop microvm@$(CLIENT_VM)
vm-client-delete: vm-client-stop
sudo rm -rf /var/lib/microvms/$(CLIENT_VM)
vm-client-connect:
ssh abra@10.0.0.2 -i ./tests/resources/local_integration_ssh
SERVER_VM := abra-server
vm-server-status:
sudo systemctl status microvm@$(SERVER_VM).service
vm-server-create:
sudo microvm -f git+file://$$(pwd) -c $(SERVER_VM)
vm-server-start:
sudo systemctl start microvm@$(SERVER_VM).service
vm-server-update:
sudo microvm -R -f git+file://$$(pwd) -u $(SERVER_VM)
vm-server-run:
sudo microvm -f git+file://$$(pwd) -r $(SERVER_VM)
vm-server-stop:
sudo systemctl stop microvm@$(SERVER_VM)
vm-server-delete: vm-server-stop
sudo rm -rf /var/lib/microvms/$(SERVER_VM)
vm-server-connect:
ssh abra@10.0.0.3 -i ./tests/resources/local_integration_ssh
test-integration-hosts:
./scripts/tests/extra_hosts
# TEST_INTEGRATION_FILES overrides the default of running all test files
ifdef TEST_INTEGRATION_FILES
test_files := $(TEST_INTEGRATION_FILES);
else
test_files := *;
endif
test-integration: build
chmod 600 ./tests/resources/local_integration_ssh
ssh-add ./tests/resources/local_integration_ssh
@bats -Tp --verbose-run tests/integration/$(test_files)
+1
View File
@@ -19,5 +19,6 @@ var (
Minor bool
NoDomainChecks bool
Patch bool
PinDigests bool
ShowUnchanged bool
)
+5 -4
View File
@@ -92,7 +92,7 @@ your private key and enter your passphrase beforehand.
log.Fatal(err)
}
mainAppVersion := imagesTmp[mainApp]
mainAppVersion := formatter.RemoveSha(imagesTmp[mainApp])
if mainAppVersion == "" {
log.Fatal(i18n.G("main app service version for %s is empty?", recipe.Name))
}
@@ -192,6 +192,9 @@ likely to change.
serviceMeta := serviceVersions[serviceName]
existingImageTag := fmt.Sprintf("%s:%s", serviceMeta.Image, serviceMeta.Tag)
if serviceMeta.Digest != "" {
existingImageTag += "@" + serviceMeta.Digest
}
newImageTag := fmt.Sprintf("%s:%s", serviceMeta.Image, imagesTmp[serviceMeta.Image])
if existingImageTag == newImageTag {
@@ -331,10 +334,8 @@ func GetImageVersions(recipe recipePkg.Recipe) (map[string]string, error) {
path = formatter.StripTagMeta(path)
var tag string
switch img.(type) {
case reference.NamedTagged:
tag = img.(reference.NamedTagged).Tag()
case reference.Named:
if service.Name == "app" {
missingTag = true
@@ -342,7 +343,7 @@ func GetImageVersions(recipe recipePkg.Recipe) (map[string]string, error) {
continue
}
services[path] = tag
services[path] = formatter.GetTagAndDigest(img)
}
if missingTag {
+42 -8
View File
@@ -130,7 +130,7 @@ interface.`),
}
for _, service := range config.Services {
img, err := reference.ParseNormalizedNamed(service.Image)
img, err := reference.ParseNormalizedNamed(formatter.RemoveSha(service.Image))
if err != nil {
log.Fatal(err)
}
@@ -178,9 +178,14 @@ interface.`),
sort.Sort(tagcmp.ByTagDesc(compatible))
var upgradeTag string
if len(compatible) == 0 && !allTags {
log.Info(i18n.G("no new versions available for %s, assuming %s is the latest (use -a/--all-tags to see all anyway)", image, tag))
continue // skip on to the next tag and don't update any compose files
if !shouldPin(service.Image, internal.PinDigests) {
continue
}
upgradeTag = tag.String()
}
catlVersions, err := recipePkg.VersionsOfService(recipe.Name, service.Name, internal.Offline)
@@ -203,9 +208,8 @@ interface.`),
log.Debug(i18n.G("detected compatible upgradable tags %s for %s", compatibleStrings, service.Name))
var upgradeTag string
_, ok := servicePins[service.Name]
if versionsPresent && ok {
if upgradeTag == "" && versionsPresent && ok {
pinnedTag := servicePins[service.Name].version
if tag.IsLessThan(pinnedTag) {
pinnedTagString := pinnedTag.String()
@@ -227,7 +231,7 @@ interface.`),
log.Fatal(i18n.G("service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!", service.Name, tag.String(), pinnedTag.String()))
continue
}
} else {
} else if upgradeTag == "" {
if bumpType != 0 {
for _, upTag := range compatible {
upElement, err := tag.UpgradeDelta(upTag)
@@ -242,7 +246,10 @@ interface.`),
}
if upgradeTag == "" {
log.Warn(i18n.G("not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants", tag.String(), compatible[0].String(), image))
continue
if !shouldPin(service.Image, internal.PinDigests) {
continue
}
upgradeTag = tag.String()
}
} else {
msg := i18n.G("upgrade to which tag? (service: %s, image: %s, tag: %s)", service.Name, image, tag)
@@ -293,12 +300,25 @@ interface.`),
}
}
if upgradeTag != "skip" {
ok, err := recipe.UpdateTag(image, upgradeTag)
var resolvedDigest string
if shouldPin(service.Image, internal.PinDigests) {
resolvedDigest, err = client.GetImageMultiplatformDigest(img, upgradeTag)
if err != nil {
log.Fatal(i18n.G("failed to resolve digest for %s:%s: %s", image, upgradeTag, err.Error()))
}
}
ok, err := recipe.UpdateTag(service.Image, upgradeTag, resolvedDigest)
if err != nil {
log.Fatal(err)
}
if ok {
log.Info(i18n.G("tag upgraded from %s to %s for %s", tag.String(), upgradeTag, image))
if shouldPin(service.Image, internal.PinDigests) {
log.Info(i18n.G("tag upgraded and pinned from %s to %s@%s for %s", tag.String(), upgradeTag, resolvedDigest, image))
} else {
log.Info(i18n.G("tag upgraded from %s to %s for %s", tag.String(), upgradeTag, image))
}
}
} else {
if !internal.NoInput {
@@ -364,6 +384,12 @@ interface.`),
},
}
// shouldPin reports whether a service image should be pinned to a digest.
// An image that is already pinned stays pinned, even without --pindigests.
func shouldPin(image string, pinDigestsFlag bool) bool {
return pinDigestsFlag || strings.Contains(image, "@sha256:")
}
var (
allTags bool
createCommit bool
@@ -417,4 +443,12 @@ func init() {
false,
i18n.G("commit changes"),
)
RecipeUpgradeCommand.Flags().BoolVarP(
&internal.PinDigests,
i18n.G("pindigests"),
i18n.GC("p", "pin container image by digest"),
true,
i18n.G("pin the container image version by manifest digest (image:tag@sha256:...)"),
)
}
+22
View File
@@ -0,0 +1,22 @@
package recipe
import "testing"
func TestShouldPin(t *testing.T) {
tests := []struct {
image string
pinDigests bool
want bool
}{
{"nginx:1.21.0", false, false},
{"nginx:1.21.0", true, true},
{"nginx:1.21.0@sha256:2bcabc23b45489fb0885d69a06ba1d648aeda973fae7bb981bafbb884165e514", false, true},
{"nginx:1.21.0@sha256:2bcabc23b45489fb0885d69a06ba1d648aeda973fae7bb981bafbb884165e514", true, true},
}
for _, test := range tests {
if got := shouldPin(test.image, test.pinDigests); got != test.want {
t.Errorf("shouldPin(%q, %v) = %v, want %v", test.image, test.pinDigests, got, test.want)
}
}
}
Generated
+38
View File
@@ -18,6 +18,27 @@
"type": "github"
}
},
"microvm": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"spectrum": "spectrum"
},
"locked": {
"lastModified": 1784666190,
"narHash": "sha256-xgfS6slV7J3baMooNN1UuBi51RIgg9y0DbCxfSA0668=",
"owner": "astro",
"repo": "microvm.nix",
"rev": "fa5340ac684cdce8a22b6d4a0bcebb0cc999275e",
"type": "github"
},
"original": {
"owner": "astro",
"repo": "microvm.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1778443072,
@@ -37,9 +58,26 @@
"root": {
"inputs": {
"flake-utils": "flake-utils",
"microvm": "microvm",
"nixpkgs": "nixpkgs"
}
},
"spectrum": {
"flake": false,
"locked": {
"lastModified": 1783694892,
"narHash": "sha256-xO8f7Qng+18FK2UlB9vcrkxCaQMCt5WjCH24aW/11eg=",
"ref": "refs/heads/main",
"rev": "24c4346e30fdea8d8e80f34aec3554a15a667d24",
"revCount": 1410,
"type": "git",
"url": "https://spectrum-os.org/git/spectrum"
},
"original": {
"type": "git",
"url": "https://spectrum-os.org/git/spectrum"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
+87 -18
View File
@@ -4,34 +4,103 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
microvm = {
url = "github:astro/microvm.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
microvm,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
packages = rec {
abra = pkgs.callPackage ./package.nix { };
default = abra;
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
publicKey = builtins.readFile ./tests/resources/local_integration_ssh.pub;
privateKey = builtins.readFile ./tests/resources/local_integration_ssh;
pathToRepo = builtins.readFile ./tests/resources/path_to_repo;
# local DNS aliases for the server host
extraHosts = builtins.readFile ./tests/resources/extra_hosts;
username = "abra";
password = "abra";
defaultDNS = [
# Quad9.net
"9.9.9.9"
"149.112.112.112"
"2620:fe::fe"
"2620:fe::9"
];
in
{
nixosConfigurations = {
abra-client = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
microvm.nixosModules.microvm
./nix/hosts/client/configuration.nix
];
specialArgs = {
inherit
publicKey
privateKey
username
password
defaultDNS
extraHosts
pathToRepo
;
};
};
apps = rec {
abra = flake-utils.lib.mkApp { drv = self.packages.${system}.abra; };
default = abra;
abra-server = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
microvm.nixosModules.microvm
./nix/hosts/server/configuration.nix
];
specialArgs = {
inherit
publicKey
privateKey
username
password
defaultDNS
;
};
};
devShells.default = pkgs.mkShell {
packages = with pkgs; [
go_1_26
gnumake
};
nixosModules = rec {
host = {
imports = [
microvm.nixosModules.host
./nix/modules/host.nix
];
};
}
);
default = host;
};
packages = rec {
abra = pkgs.callPackage ./nix/package.nix { };
default = abra;
};
apps = rec {
abra = flake-utils.lib.mkApp { drv = self.packages.${system}.abra; };
default = abra;
};
devShells.${system}.default = pkgs.mkShell {
# testing env variables
BATS_LIB_PATH = "~/.local/share/bats/";
TEST_SERVER = "abra.local";
ABRA_DIR = "$HOME/.abra_test";
packages = with pkgs; [
go_1_26
gnumake
gopls
];
};
};
}
+12 -11
View File
@@ -13,14 +13,15 @@ require (
github.com/docker/cli v28.4.0+incompatible
github.com/docker/docker v28.5.2+incompatible
github.com/docker/go-units v0.5.0
github.com/go-git/go-git/v5 v5.19.2
github.com/go-git/go-git/v5 v5.19.3
github.com/google/go-cmp v0.7.0
github.com/leonelquinteros/gotext v1.7.2
github.com/moby/sys/signal v0.7.1
github.com/moby/term v0.5.2
github.com/pkg/errors v0.9.1
github.com/regclient/regclient v0.11.5
github.com/schollz/progressbar/v3 v3.19.1
golang.org/x/term v0.46.0
golang.org/x/term v0.47.0
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.2
)
@@ -59,7 +60,7 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.9.0 // indirect
github.com/go-git/go-billy/v5 v5.9.2 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
@@ -72,8 +73,7 @@ require (
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.6.0 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/klauspost/compress v1.18.6 // indirect
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
@@ -95,7 +95,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/runc v1.1.13 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
github.com/pjbgf/sha1cd v0.6.0 // indirect
github.com/pjbgf/sha1cd v0.7.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.20.1 // indirect
@@ -104,6 +104,7 @@ require (
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/skeema/knownhosts v1.3.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
@@ -122,10 +123,10 @@ require (
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/crypto v0.53.0 // indirect
golang.org/x/crypto v0.56.0 // indirect
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/text v0.39.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/text v0.41.0 // indirect
golang.org/x/time v0.15.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
@@ -149,11 +150,11 @@ require (
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/spf13/cobra v1.10.1
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.12.1
github.com/theupdateframework/notary v0.7.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
golang.org/x/sys v0.48.0
golang.org/x/sys v0.49.0
)
replace github.com/docker/cli v28.4.0+incompatible => git.coopcloud.tech/toolshed/docker-cli v28.5.3-0.20260202112816-30df2d0b3a00+incompatible
+28 -20
View File
@@ -388,12 +388,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA=
github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw=
github.com/go-git/go-billy/v5 v5.9.2 h1:OXFSRyz4g20upsGDJgQG9Bak1l/ZEv8GHVYB52O71sE=
github.com/go-git/go-billy/v5 v5.9.2/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.19.2 h1:wkfn7vOlUBu8ivAWKBWisTiwJK4jYHzTF8Ndv1LyGqY=
github.com/go-git/go-git/v5 v5.19.2/go.mod h1:QqCBE1EFN5ddFmrliLQ3/ntRCUjZU3EJuwuB/jWEHjk=
github.com/go-git/go-git/v5 v5.19.3 h1:qHttbZ+Am7wEjdTpR1XMQ4rl42FK9SIXzZ4o9x7s6M4=
github.com/go-git/go-git/v5 v5.19.3/go.mod h1:Ye4C8dVigkqrv9UsB4NMdSLV4yAIkLiIhW61gcOyhl4=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -426,6 +426,8 @@ github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
@@ -583,10 +585,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -698,6 +698,8 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/olareg/olareg v0.2.1 h1:RPHGIaqlVWPbKAsOYUj7e2WfEEW5M7F8I6hKMrwd4jU=
github.com/olareg/olareg v0.2.1/go.mod h1:dhr8QetC7U7jJ2m93oxDhEEOKCRbPgOK1oGyKfB4QNo=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -752,8 +754,8 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pjbgf/sha1cd v0.7.0 h1:ZRNPKHj+gfkLBf0KJv/p1Hmz+7bqCT5o311YX0nq+DA=
github.com/pjbgf/sha1cd v0.7.0/go.mod h1:pKR5Li+qTCo+ebqITZfwPlJGoCFCvSO00qxjbNtTUFQ=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -794,6 +796,8 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
github.com/regclient/regclient v0.11.5 h1:OHRsXO0F3qHGfa4HEUv+EkMH9NXNcCTBKjNzyC/UhIA=
github.com/regclient/regclient v0.11.5/go.mod h1:DZUOfIT14WFTK2Pj4vjd93avy9O4Fdpjrf9ir23TbRE=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -857,6 +861,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
github.com/sudo-bmitch/oci-digest v0.1.2 h1:are0qzWTsFZGZ3Uvdi9OSztJszSWaab6iqquMEEB7rw=
github.com/sudo-bmitch/oci-digest v0.1.2/go.mod h1:SH6l5OIe0islKBZBedjiPOeET/0QwGL+/oYfQt51uQo=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
@@ -868,6 +874,8 @@ github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnH
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
@@ -965,8 +973,8 @@ golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1042,8 +1050,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1138,13 +1146,13 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE=
golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc=
golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM=
golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1154,8 +1162,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-54
View File
@@ -1,54 +0,0 @@
{
buildGo126Module,
fetchgit,
lib,
installShellFiles,
}:
buildGo126Module rec {
pname = "abra";
version = "0.13.0-beta";
rev = "06a57ded025a43c80f94d4e65299add8a31830dc";
src = fetchgit {
url = "https://git.coopcloud.tech/toolshed/abra.git";
tag = version;
hash = "sha256-rgoK0TY0WLSQ39lPvVM80zW/qJF40VFBSxYDOaKXZQo=";
};
vendorHash = null;
nativeBuildInputs = [
installShellFiles
];
env.CGO_ENABLED = 0;
buildPhase = ''
runHook preBuild
go build -ldflags="-s -w -X 'main.Commit=${rev}' -X 'main.Version=${version}'" ./cmd/abra
runHook postBuild
'';
installPhase = ''
runHook preInstall
install -D abra $out/bin/abra
runHook postInstall
'';
postInstall = ''
export ABRA_DIR="$out"
$out/bin/abra autocomplete bash >abra.bash
$out/bin/abra autocomplete fish >abra.fish
$out/bin/abra autocomplete zsh >abra.zsh
installShellCompletion abra.{bash,fish,zsh}
'';
meta = with lib; {
description = "The Co-op Cloud utility belt";
homepage = "https://docs.coopcloud.tech/abra";
changelog = "https://git.coopcloud.tech/toolshed/abra/releases/tag/${version}";
mainProgram = "abra";
license = licenses.gpl3Plus;
maintainers = "devydave";
};
}
+32
View File
@@ -9,6 +9,8 @@ import (
"github.com/containers/image/docker"
"github.com/containers/image/types"
"github.com/distribution/reference"
"github.com/regclient/regclient"
"github.com/regclient/regclient/types/ref"
)
// GetRegistryTags retrieves all tags of an image from a container registry.
@@ -28,3 +30,33 @@ func GetRegistryTags(img reference.Named) ([]string, error) {
return tags, nil
}
// GetImageDigest resolves the content digest (sha256:...) for a specific
// image:tag reference by querying the registry manifest. This gives us a
// digest to pin to while keeping support for multi-arch deployments
// See: https://docs.docker.com/build/building/multi-platform/#why-multi-platform-builds
// and: https://docs.docker.com/dhi/explore/security-concepts/digests/#multi-platform-images-and-manifests
func GetImageMultiplatformDigest(img reference.Named, tag string) (string, error) {
tagged, err := reference.WithTag(reference.TrimNamed(img), tag)
if err != nil {
return "", errors.New(i18n.G("failed to parse image %s:%s, saw: %s", img.Name(), tag, err.Error()))
}
r, err := ref.New(tagged.String())
if err != nil {
return "", errors.New(i18n.G("failed to parse image %s:%s, saw: %s", img.Name(), tag, err.Error()))
}
rc := regclient.New(regclient.WithDockerCreds())
m, err := rc.ManifestHead(context.Background(), r)
if err != nil {
return "", errors.New(i18n.G("failed to fetch manifest for %s:%s, saw: %s", img.Name(), tag, err.Error()))
}
digest := m.GetDescriptor().Digest.String()
if digest == "" {
return "", errors.New(i18n.G("registry did not return a digest for %s:%s", img.Name(), tag))
}
return digest, nil
}
+6 -2
View File
@@ -171,7 +171,7 @@ func GetImagesForStack(cl *dockerClient.Client, app appPkg.App) (map[string]stri
images[imageBaseName] = ""
continue
}
imageTag := namedTag.Tag()
imageTag := formatter.GetTagAndDigest(namedTag)
existingImageVersion, ok := images[imageBaseName]
if !ok {
@@ -294,7 +294,7 @@ func GatherImagesForDeploy(cl *dockerClient.Client, app appPkg.App, compose *com
newImages[imageBaseName] = ""
continue
}
imageTag := namedTag.Tag()
imageTag := formatter.GetTagAndDigest(namedTag)
existingImageVersion, ok := newImages[imageBaseName]
if !ok {
@@ -312,6 +312,10 @@ func GatherImagesForDeploy(cl *dockerClient.Client, app appPkg.App, compose *com
var imageInfo []string
for newImageName, newImageVersion := range newImages {
if currentVersion, exists := currentImages[newImageName]; exists {
// Swarm adds a resolved digest even for unpinned compose images.
if !strings.Contains(newImageVersion, "@") {
currentVersion = formatter.RemoveSha(currentVersion)
}
if currentVersion == newImageVersion {
if showUnchanged {
imageInfo = append(imageInfo, i18n.G("%s: %s (unchanged)", formatter.StripTagMeta(newImageName), newImageVersion))
+17 -2
View File
@@ -11,6 +11,7 @@ import (
"coopcloud.tech/abra/pkg/i18n"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/lipgloss/table"
"github.com/distribution/reference"
"github.com/docker/go-units"
"golang.org/x/term"
@@ -34,11 +35,24 @@ func SmallSHA(hash string) string {
return hash[:8]
}
// RemoveSha remove image sha from a string that are added in some docker outputs
// RemoveSha remove image sha (digest suffix) from a string that are added in some docker outputs
// e.g., "image:tag@sha256:..." -> "image:tag"
func RemoveSha(str string) string {
return strings.Split(str, "@")[0]
}
// GetTagAndDigest returns the tag and any digest for comparison and display.
func GetTagAndDigest(image reference.Named) string {
var version string
if tagged, ok := image.(reference.NamedTagged); ok {
version = tagged.Tag()
}
if pinned, ok := image.(reference.Digested); ok {
version += "@" + pinned.Digest().String()
}
return version
}
// HumanDuration from docker/cli RunningFor() to be accessible outside of the class
func HumanDuration(timestamp int64) string {
date := time.Unix(timestamp, 0)
@@ -222,7 +236,8 @@ func CreateProgressbar(length int, title string) *progressbar.ProgressBar {
)
}
// StripTagMeta strips front-matter image tag data that we don't need for parsing.
// StripTagMeta strips front-matter image tag data that we don't need for parsing,
// like registry host and library prefix.
func StripTagMeta(image string) string {
originalImage := image
+26
View File
@@ -3,9 +3,35 @@ package formatter
import (
"testing"
"github.com/distribution/reference"
"github.com/stretchr/testify/assert"
)
func TestRemoveSha(t *testing.T) {
assert.Equal(t, "nginx:latest", RemoveSha("nginx:latest@sha256:1234567890abcdef"))
assert.Equal(t, "nginx:latest", RemoveSha("nginx:latest"))
assert.Equal(t, "my-repo/my-image:v1", RemoveSha("my-repo/my-image:v1@sha256:abcdef1234567890"))
assert.Equal(t, "my-repo/my-image", RemoveSha("my-repo/my-image@sha256:abcdef1234567890"))
}
func TestBoldDirtyDefault(t *testing.T) {
assert.Equal(t, "foo", BoldDirtyDefault("foo"))
}
func TestGetTagAndDigest(t *testing.T) {
digest := "sha256:908dc23e643a1447dbfb2e189ed268bfde6a51a5bf9a34d3dd3440a24f58ccf7"
for _, tc := range []struct{ image, want string }{
{"nginx:1.21.6", "1.21.6"},
{"ghcr.io/foo/bar:1.0@" + digest, "1.0@" + digest},
{"nginx@" + digest, "@" + digest},
{"nginx", ""},
} {
img, err := reference.ParseNormalizedNamed(tc.image)
if err != nil {
t.Fatal(err)
}
if got := GetTagAndDigest(img); got != tc.want {
t.Errorf("%s: got %s, want %s", tc.image, got, tc.want)
}
}
}
+160 -94
View File
@@ -7,7 +7,7 @@
msgid ""
msgstr "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2026-08-31 19:21+0000\n"
"POT-Creation-Date: 2026-10-02 16:58-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -165,7 +165,7 @@ msgid " # standard rollback\n"
" abra app rollback 1312.net 2.0.0+1.2.3"
msgstr ""
#: ./pkg/formatter/formatter.go:46
#: ./pkg/formatter/formatter.go:47
msgid " ago"
msgstr ""
@@ -219,7 +219,7 @@ msgstr ""
msgid "%s created (version: %s)"
msgstr ""
#: ./cli/recipe/upgrade.go:335
#: ./cli/recipe/upgrade.go:364
#, c-format
msgid "%s currently has these unstaged changes 👇"
msgstr ""
@@ -304,7 +304,7 @@ msgstr ""
msgid "%s has no secrets to generate, skipping..."
msgstr ""
#: ./pkg/recipe/compose.go:107
#: ./pkg/recipe/compose.go:110
#, c-format
msgid "%s has no version label? try running \"abra recipe sync %s\" first?"
msgstr ""
@@ -324,12 +324,12 @@ msgstr ""
msgid "%s is already fetched"
msgstr ""
#: ./pkg/recipe/compose.go:233
#: ./pkg/recipe/compose.go:374
#, c-format
msgid "%s is already set, nothing to do?"
msgstr ""
#: ./pkg/recipe/compose.go:72
#: ./pkg/recipe/compose.go:75
#, c-format
msgid "%s is missing a compose.yml or compose.*.yml file?"
msgstr ""
@@ -568,12 +568,12 @@ msgstr ""
msgid "--local detected, running %s on local work station"
msgstr ""
#: ./pkg/lint/recipe.go:261 ./pkg/recipe/files.go:18
#: ./pkg/lint/recipe.go:268 ./pkg/recipe/files.go:18
#, c-format
msgid ".env.sample for %s couldn't be read: %s"
msgstr ""
#: ./pkg/lint/recipe.go:133
#: ./pkg/lint/recipe.go:140
msgid ".env.sample provided"
msgstr ""
@@ -638,17 +638,17 @@ msgstr ""
msgid "CHAOS DEPLOY"
msgstr ""
#: ./pkg/recipe/compose.go:47
#: ./pkg/recipe/compose.go:50
#, c-format
msgid "COMPOSE_FILE (=\"%s\") parsing failed?"
msgstr ""
#: ./pkg/recipe/compose.go:58
#: ./pkg/recipe/compose.go:61
#, c-format
msgid "COMPOSE_FILE detected (%s), loading %s"
msgstr ""
#: ./pkg/recipe/compose.go:38
#: ./pkg/recipe/compose.go:41
#, c-format
msgid "COMPOSE_FILE detected, loading %s"
msgstr ""
@@ -739,7 +739,7 @@ msgid "Create a new version of a recipe.\n"
" ssh-add ~/.ssh/<my-ssh-private-key-for-git-coopcloud-tech>"
msgstr ""
#: ./pkg/formatter/formatter.go:134
#: ./pkg/formatter/formatter.go:135
msgid "CreateOverview: only accepts rows of len == 2"
msgstr ""
@@ -1644,7 +1644,7 @@ msgid "\n"
"\n"
msgstr ""
#: ./pkg/lint/recipe.go:203
#: ./pkg/lint/recipe.go:210
#, c-format
msgid "\n"
"lint %s: %s"
@@ -1687,7 +1687,7 @@ msgctxt "recipe fetch"
msgid "a"
msgstr ""
#: ./cli/recipe/upgrade.go:408
#: ./cli/recipe/upgrade.go:444
msgctxt "recipe upgrade"
msgid "a"
msgstr ""
@@ -1737,7 +1737,7 @@ msgstr ""
msgid "accepts at most 2 args with --local/-l"
msgstr ""
#: ./pkg/recipe/compose.go:249
#: ./pkg/recipe/compose.go:390
#, c-format
msgid "add '- \"%s\"' manually to the 'app' service in %s"
msgstr ""
@@ -1764,11 +1764,15 @@ msgstr ""
msgid "all containers did really go away"
msgstr ""
#: ./pkg/lint/recipe.go:131
msgid "all images use a digest"
msgstr ""
#: ./pkg/lint/recipe.go:82
msgid "all images use a tag"
msgstr ""
#: ./pkg/lint/recipe.go:162
#: ./pkg/lint/recipe.go:169
msgid "all services have images"
msgstr ""
@@ -1780,7 +1784,7 @@ msgstr ""
msgid "all-services"
msgstr ""
#: ./cli/recipe/upgrade.go:407
#: ./cli/recipe/upgrade.go:443
msgid "all-tags"
msgstr ""
@@ -1919,7 +1923,7 @@ msgstr ""
msgid "c"
msgstr ""
#: ./cli/recipe/upgrade.go:416
#: ./cli/recipe/upgrade.go:452
msgctxt "recipe upgrade"
msgid "c"
msgstr ""
@@ -1928,7 +1932,7 @@ msgstr ""
msgid "can not insert from file and read from stdin"
msgstr ""
#: ./cli/recipe/upgrade.go:323
#: ./cli/recipe/upgrade.go:352
#, c-format
msgid "can upgrade service: %s, image: %s, tag: %s ::"
msgstr ""
@@ -2108,7 +2112,7 @@ msgstr ""
msgid "chore: publish new catalogue release changes"
msgstr ""
#: ./cli/recipe/upgrade.go:352
#: ./cli/recipe/upgrade.go:381
msgid "chore: update image tags"
msgstr ""
@@ -2170,19 +2174,19 @@ msgstr ""
msgid "commandconn: starting %s with %v"
msgstr ""
#: ./cli/recipe/upgrade.go:415
#: ./cli/recipe/upgrade.go:451
msgid "commit"
msgstr ""
#: ./cli/recipe/upgrade.go:418
#: ./cli/recipe/upgrade.go:454
msgid "commit changes"
msgstr ""
#: ./cli/recipe/upgrade.go:342
#: ./cli/recipe/upgrade.go:371
msgid "commit changes?"
msgstr ""
#: ./cli/recipe/upgrade.go:356
#: ./cli/recipe/upgrade.go:385
#, c-format
msgid "committed changes as '%s'"
msgstr ""
@@ -2221,7 +2225,7 @@ msgstr ""
msgid "config not found: %s"
msgstr ""
#: ./pkg/lint/recipe.go:169
#: ./pkg/lint/recipe.go:176
msgid "config version are vendored"
msgstr ""
@@ -2230,14 +2234,14 @@ msgstr ""
msgid "connection timed out for %s"
msgstr ""
#: ./pkg/recipe/compose.go:191
#: ./pkg/recipe/compose.go:146
#, c-format
msgid "considering %s config(s) for label update"
msgid "considering %s config(s) for image update"
msgstr ""
#: ./pkg/recipe/compose.go:123
#: ./pkg/recipe/compose.go:332
#, c-format
msgid "considering %s config(s) for tag update"
msgid "considering %s config(s) for label update"
msgstr ""
#: ./cli/app/undeploy.go:141 ./cli/server/prune.go:53
@@ -2258,7 +2262,7 @@ msgstr ""
msgid "context lacks Docker endpoint"
msgstr ""
#: ./pkg/recipe/compose.go:229
#: ./pkg/recipe/compose.go:370
#, c-format
msgid "coop-cloud.${STACK_NAME}.version=%s"
msgstr ""
@@ -2303,7 +2307,7 @@ msgstr ""
msgid "create <domain> [flags]"
msgstr ""
#: ./pkg/lint/recipe.go:134
#: ./pkg/lint/recipe.go:141
msgid "create an example .env.sample"
msgstr ""
@@ -2418,7 +2422,7 @@ msgstr ""
msgid "deploy in progress 🟠"
msgstr ""
#: ./pkg/lint/recipe.go:147
#: ./pkg/lint/recipe.go:154
msgid "deploy labels stanza present"
msgstr ""
@@ -2461,11 +2465,11 @@ msgstr ""
msgid "detected %s as tags for recipe %s"
msgstr ""
#: ./pkg/formatter/formatter.go:80
#: ./pkg/formatter/formatter.go:81
msgid "detected ABRA_CI=1"
msgstr ""
#: ./cli/recipe/upgrade.go:204
#: ./cli/recipe/upgrade.go:208
#, c-format
msgid "detected compatible upgradable tags %s for %s"
msgstr ""
@@ -2644,7 +2648,7 @@ msgstr ""
msgid "ensure 'version: \"3.8\"' in compose configs"
msgstr ""
#: ./pkg/lint/recipe.go:163
#: ./pkg/lint/recipe.go:170
msgid "ensure \"image: ...\" set on all services"
msgstr ""
@@ -2658,7 +2662,7 @@ msgstr ""
msgid "ensuring env version %s"
msgstr ""
#: ./cli/recipe/upgrade.go:285
#: ./cli/recipe/upgrade.go:291
msgid "enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode is enabled"
msgstr ""
@@ -2682,7 +2686,7 @@ msgstr ""
msgid "err: %v, "
msgstr ""
#: ./cli/recipe/lint.go:82 ./cli/recipe/lint.go:144 ./pkg/lint/recipe.go:132 ./pkg/lint/recipe.go:139 ./pkg/lint/recipe.go:146 ./pkg/lint/recipe.go:153 ./pkg/lint/recipe.go:161 ./pkg/lint/recipe.go:168 ./pkg/lint/recipe.go:175
#: ./cli/recipe/lint.go:82 ./cli/recipe/lint.go:144 ./pkg/lint/recipe.go:139 ./pkg/lint/recipe.go:146 ./pkg/lint/recipe.go:153 ./pkg/lint/recipe.go:160 ./pkg/lint/recipe.go:168 ./pkg/lint/recipe.go:175 ./pkg/lint/recipe.go:182
msgid "error"
msgstr ""
@@ -2801,6 +2805,11 @@ msgstr ""
msgid "failed to create volume %s on %s: %s"
msgstr ""
#: ./pkg/client/registry.go:54
#, c-format
msgid "failed to fetch manifest for %s:%s, saw: %s"
msgstr ""
#: ./pkg/secret/secret.go:104
#, c-format
msgid "failed to generate random bytes: %w"
@@ -2839,11 +2848,16 @@ msgstr ""
msgid "failed to migrate app config: %s"
msgstr ""
#: ./pkg/client/registry.go:20
#: ./pkg/client/registry.go:22
#, c-format
msgid "failed to parse image %s, saw: %s"
msgstr ""
#: ./pkg/client/registry.go:45
#, c-format
msgid "failed to parse image %s:%s, saw: %s"
msgstr ""
#: ./pkg/recipe/git.go:437
#, c-format
msgid "failed to parse image for %s in %s: %s"
@@ -2898,6 +2912,11 @@ msgstr ""
msgid "failed to resize tty, using default size"
msgstr ""
#: ./cli/recipe/upgrade.go:313
#, c-format
msgid "failed to resolve digest for %s:%s: %s"
msgstr ""
#: ./cli/app/new.go:138
#, c-format
msgid "failed to retrieve latest commit for %s: %s"
@@ -3206,16 +3225,26 @@ msgstr ""
msgid "ignore uncommitted recipes changes"
msgstr ""
#: ./pkg/recipe/compose.go:248
#, c-format
msgid "image %s for %s uses a variable, skipping update, please update it manually"
msgstr ""
#: ./pkg/recipe/compose.go:183
#, c-format
msgid "image for %s uses a variable, skipping update, please update %s manually"
msgstr ""
#: ./cli/app/undeploy.go:156 ./cli/server/prune.go:74
#, c-format
msgid "images pruned: %d; space reclaimed: %s"
msgstr ""
#: ./pkg/lint/recipe.go:148
#: ./pkg/lint/recipe.go:155
msgid "include \"deploy: labels: ...\" stanza"
msgstr ""
#: ./pkg/lint/recipe.go:155
#: ./pkg/lint/recipe.go:162
msgid "include \"traefik.enable=true\" deploy label"
msgstr ""
@@ -3291,15 +3320,15 @@ msgstr ""
msgid "including VOLUMES=%v in backupbot exec invocation"
msgstr ""
#: ./cli/recipe/release.go:634 ./cli/recipe/upgrade.go:378
#: ./cli/recipe/release.go:634 ./cli/recipe/upgrade.go:414
msgid "increase the major part of the version"
msgstr ""
#: ./cli/recipe/release.go:642 ./cli/recipe/upgrade.go:386
#: ./cli/recipe/release.go:642 ./cli/recipe/upgrade.go:422
msgid "increase the minor part of the version"
msgstr ""
#: ./cli/recipe/release.go:650 ./cli/recipe/upgrade.go:394
#: ./cli/recipe/release.go:650 ./cli/recipe/upgrade.go:430
msgid "increase the patch part of the version"
msgstr ""
@@ -3369,7 +3398,12 @@ msgstr ""
msgid "invalid detach escape keys, using default: %s"
msgstr ""
#: ./pkg/lint/recipe.go:504
#: ./pkg/recipe/compose.go:308
#, c-format
msgid "invalid digest %s, want sha256:<64 hex digits>"
msgstr ""
#: ./pkg/lint/recipe.go:533
msgid "invalid lightweight tag detected"
msgstr ""
@@ -3430,12 +3464,12 @@ msgstr ""
msgid "lint <recipe> [flags]"
msgstr ""
#: ./pkg/lint/recipe.go:187
#: ./pkg/lint/recipe.go:194
#, c-format
msgid "linting for critical errors in %s configs"
msgstr ""
#: ./pkg/lint/recipe.go:215
#: ./pkg/lint/recipe.go:222
#, c-format
msgid "linting successful, %s is well configured"
msgstr ""
@@ -3464,7 +3498,7 @@ msgstr ""
msgid "list [flags]"
msgstr ""
#: ./cli/recipe/upgrade.go:410
#: ./cli/recipe/upgrade.go:446
msgid "list all tags, not just upgrades"
msgstr ""
@@ -3534,11 +3568,11 @@ msgstr ""
#. with no spaces in between
#. translators: `abra man` aliases. use a comma separated list of aliases
#. with no spaces in between
#: ./cli/app/list.go:319 ./cli/app/move.go:34 ./cli/app/ps.go:205 ./cli/app/secret.go:553 ./cli/app/secret.go:649 ./cli/recipe/list.go:104 ./cli/recipe/upgrade.go:400 ./cli/recipe/version.go:139 ./cli/run.go:152 ./cli/server/list.go:106
#: ./cli/app/list.go:319 ./cli/app/move.go:34 ./cli/app/ps.go:205 ./cli/app/secret.go:553 ./cli/app/secret.go:649 ./cli/recipe/list.go:104 ./cli/recipe/upgrade.go:436 ./cli/recipe/version.go:139 ./cli/run.go:152 ./cli/server/list.go:106
msgid "m"
msgstr ""
#: ./cli/app/list.go:318 ./cli/app/ps.go:204 ./cli/app/secret.go:552 ./cli/app/secret.go:648 ./cli/recipe/list.go:103 ./cli/recipe/upgrade.go:399 ./cli/recipe/version.go:138 ./cli/server/list.go:105
#: ./cli/app/list.go:318 ./cli/app/ps.go:204 ./cli/app/secret.go:552 ./cli/app/secret.go:648 ./cli/recipe/list.go:103 ./cli/recipe/upgrade.go:435 ./cli/recipe/version.go:138 ./cli/server/list.go:105
msgid "machine"
msgstr ""
@@ -3547,7 +3581,7 @@ msgstr ""
msgid "main app service version for %s is empty?"
msgstr ""
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:66 ./cli/internal/recipe.go:80 ./cli/recipe/release.go:631 ./cli/recipe/upgrade.go:375
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:66 ./cli/internal/recipe.go:80 ./cli/recipe/release.go:631 ./cli/recipe/upgrade.go:411
msgid "major"
msgstr ""
@@ -3576,7 +3610,7 @@ msgstr ""
msgid "migrating app config from %s to %s"
msgstr ""
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:68 ./cli/internal/recipe.go:82 ./cli/recipe/release.go:639 ./cli/recipe/upgrade.go:383
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:68 ./cli/internal/recipe.go:82 ./cli/recipe/release.go:639 ./cli/recipe/upgrade.go:419
msgid "minor"
msgstr ""
@@ -3627,7 +3661,7 @@ msgstr ""
msgid "name"
msgstr ""
#: ./pkg/lint/recipe.go:141
#: ./pkg/lint/recipe.go:148
msgid "name a servce 'app'"
msgstr ""
@@ -3705,7 +3739,7 @@ msgstr ""
msgid "no .git directory in %s?"
msgstr ""
#: ./pkg/recipe/compose.go:29
#: ./pkg/recipe/compose.go:32
#, c-format
msgid "no COMPOSE_FILE detected, loading default: %s"
msgstr ""
@@ -3731,7 +3765,7 @@ msgstr ""
msgid "no changes discovered in %s, nothing to publish?"
msgstr ""
#: ./cli/recipe/upgrade.go:361
#: ./cli/recipe/upgrade.go:390
msgid "no changes, skip creating commit"
msgstr ""
@@ -3756,7 +3790,7 @@ msgstr ""
msgid "no domain provided"
msgstr ""
#: ./pkg/recipe/compose.go:248
#: ./pkg/recipe/compose.go:389
msgid "no existing label found, automagic insertion not supported yet"
msgstr ""
@@ -3765,7 +3799,7 @@ msgstr ""
msgid "no git tags found for %s"
msgstr ""
#: ./cli/recipe/upgrade.go:182
#: ./cli/recipe/upgrade.go:183
#, c-format
msgid "no new versions available for %s, assuming %s is the latest (use -a/--all-tags to see all anyway)"
msgstr ""
@@ -3897,12 +3931,12 @@ msgstr ""
msgid "not requesting a remote TTY"
msgstr ""
#: ./cli/recipe/upgrade.go:305
#: ./cli/recipe/upgrade.go:334
#, c-format
msgid "not upgrading %s, skipping as requested"
msgstr ""
#: ./cli/recipe/upgrade.go:244
#: ./cli/recipe/upgrade.go:247
#, c-format
msgid "not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants"
msgstr ""
@@ -3928,11 +3962,11 @@ msgstr ""
msgid "one of <src>/<dest> arguments must take $SERVICE:$PATH form"
msgstr ""
#: ./pkg/lint/recipe.go:140
#: ./pkg/lint/recipe.go:147
msgid "one service named 'app'"
msgstr ""
#: ./pkg/lint/recipe.go:176
#: ./pkg/lint/recipe.go:183
msgid "only annotated tags used for recipe version"
msgstr ""
@@ -3956,16 +3990,16 @@ msgstr ""
msgid "p"
msgstr ""
#: ./cli/recipe/upgrade.go:460
msgctxt "pin container image by digest"
msgid "p"
msgstr ""
#: ./cli/recipe/upgrade.go:163
#, c-format
msgid "parsed %s for %s"
msgstr ""
#: ./pkg/recipe/compose.go:159
#, c-format
msgid "parsed %s from %s"
msgstr ""
#: ./cli/app/cmd.go:134 ./cli/app/cmd.go:180
#, c-format
msgid "parsed following command arguments: %s"
@@ -3999,7 +4033,7 @@ msgstr ""
msgid "pass command not found on $PATH, is it installed?"
msgstr ""
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:70 ./cli/internal/recipe.go:84 ./cli/recipe/release.go:647 ./cli/recipe/upgrade.go:391
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:70 ./cli/internal/recipe.go:84 ./cli/recipe/release.go:647 ./cli/recipe/upgrade.go:427
msgid "patch"
msgstr ""
@@ -4015,6 +4049,14 @@ msgstr ""
msgid "perform action without further prompt"
msgstr ""
#: ./cli/recipe/upgrade.go:462
msgid "pin the container image version by manifest digest (image:tag@sha256:...)"
msgstr ""
#: ./cli/recipe/upgrade.go:459
msgid "pindigests"
msgstr ""
#. translators: `abra app env pull` aliases. use a comma separated list of
#. aliases with no spaces in between
#: ./cli/app/env.go:39
@@ -4053,7 +4095,7 @@ msgstr ""
msgid "prefer offline & filesystem access"
msgstr ""
#: ./cli/app/list.go:321 ./cli/app/ps.go:207 ./cli/app/secret.go:555 ./cli/app/secret.go:651 ./cli/recipe/list.go:106 ./cli/recipe/upgrade.go:402 ./cli/recipe/version.go:141 ./cli/server/list.go:108
#: ./cli/app/list.go:321 ./cli/app/ps.go:207 ./cli/app/secret.go:555 ./cli/app/secret.go:651 ./cli/recipe/list.go:106 ./cli/recipe/upgrade.go:438 ./cli/recipe/version.go:141 ./cli/server/list.go:108
msgid "print machine-readable output"
msgstr ""
@@ -4206,7 +4248,7 @@ msgstr ""
msgid "recipe %s does not exist?"
msgstr ""
#: ./pkg/lint/recipe.go:212
#: ./pkg/lint/recipe.go:219
#, c-format
msgid "recipe '%s' failed lint checks:\n"
"%s"
@@ -4230,6 +4272,11 @@ msgstr ""
msgid "ref"
msgstr ""
#: ./pkg/client/registry.go:59
#, c-format
msgid "registry did not return a digest for %s:%s"
msgstr ""
#. translators: `recipe release` command
#: ./cli/recipe/release.go:35
msgid "release <recipe> [version] [flags]"
@@ -4328,6 +4375,11 @@ msgstr ""
msgid "removing configs failed: %s"
msgstr ""
#: ./pkg/recipe/compose.go:122
#, c-format
msgid "removing digest from %s for %s, use --pindigests to pin the new tag"
msgstr ""
#: ./pkg/upstream/stack/remove.go:148
#, c-format
msgid "removing network %s"
@@ -4353,7 +4405,7 @@ msgstr ""
msgid "removing volumes failed: %s"
msgstr ""
#: ./pkg/lint/recipe.go:177
#: ./pkg/lint/recipe.go:184
msgid "replace lightweight tag with annotated tag"
msgstr ""
@@ -4436,7 +4488,7 @@ msgstr ""
msgid "retrieved %s as target container on %s"
msgstr ""
#: ./pkg/recipe/compose.go:59
#: ./pkg/recipe/compose.go:62
#, c-format
msgid "retrieved %s configs for %s"
msgstr ""
@@ -4607,7 +4659,7 @@ msgstr ""
msgid "secret %s is > %d chars when combined with %s"
msgstr ""
#: ./pkg/lint/recipe.go:481
#: ./pkg/lint/recipe.go:510
#, c-format
msgid "secret %s is longer than 12 characters"
msgstr ""
@@ -4723,12 +4775,12 @@ msgstr ""
msgid "serverAdd: cleanUp: unable to list files in %s: %s"
msgstr ""
#: ./cli/recipe/upgrade.go:227
#: ./cli/recipe/upgrade.go:230
#, c-format
msgid "service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!"
msgstr ""
#: ./cli/recipe/upgrade.go:223
#: ./cli/recipe/upgrade.go:226
#, c-format
msgid "service %s, image %s pinned to %s, no compatible upgrade found"
msgstr ""
@@ -4969,7 +5021,7 @@ msgstr ""
msgid "store secrets in a local pass store"
msgstr ""
#: ./pkg/formatter/formatter.go:238
#: ./pkg/formatter/formatter.go:239
#, c-format
msgid "stripped %s to %s for parsing"
msgstr ""
@@ -5007,7 +5059,7 @@ msgstr ""
msgid "sync [flags]"
msgstr ""
#: ./pkg/recipe/compose.go:243
#: ./pkg/recipe/compose.go:384
#, c-format
msgid "synced label %s to service %s"
msgstr ""
@@ -5025,7 +5077,12 @@ msgstr ""
msgid "tag at commit %s is unannotated or otherwise broken"
msgstr ""
#: ./cli/recipe/upgrade.go:301
#: ./cli/recipe/upgrade.go:327
#, c-format
msgid "tag upgraded and pinned from %s to %s@%s for %s"
msgstr ""
#: ./cli/recipe/upgrade.go:329
#, c-format
msgid "tag upgraded from %s to %s for %s"
msgstr ""
@@ -5099,7 +5156,7 @@ msgstr ""
msgid "toggle non-interactive mode"
msgstr ""
#: ./pkg/lint/recipe.go:154
#: ./pkg/lint/recipe.go:161
msgid "traefik routing enabled"
msgstr ""
@@ -5212,7 +5269,7 @@ msgstr ""
msgid "unable to determine server of app %s, please pass --server/-s"
msgstr ""
#: ./cli/recipe/upgrade.go:252
#: ./cli/recipe/upgrade.go:258
#, c-format
msgid "unable to determine versioning semantics of %s, listing all tags"
msgstr ""
@@ -5227,6 +5284,11 @@ msgstr ""
msgid "unable to fetch tags in %s: %s"
msgstr ""
#: ./pkg/recipe/compose.go:238 ./pkg/recipe/compose.go:245 ./pkg/recipe/compose.go:255 ./pkg/recipe/compose.go:260
#, c-format
msgid "unable to find image %s for %s, please update it manually"
msgstr ""
#: ./cli/app/move.go:301
#, c-format
msgid "unable to get container matching %s: %s"
@@ -5247,7 +5309,7 @@ msgstr ""
msgid "unable to inspect container for %s: %s"
msgstr ""
#: ./pkg/lint/recipe.go:496
#: ./pkg/lint/recipe.go:525
#, c-format
msgid "unable to list local tags for %s"
msgstr ""
@@ -5261,7 +5323,7 @@ msgstr ""
msgid "unable to look up server context for %s: %s"
msgstr ""
#: ./cli/recipe/fetch.go:77 ./pkg/git/read.go:26 ./pkg/lint/recipe.go:491 ./pkg/recipe/git.go:243
#: ./cli/recipe/fetch.go:77 ./pkg/git/read.go:26 ./pkg/lint/recipe.go:520 ./pkg/recipe/git.go:243
#, c-format
msgid "unable to open %s: %s"
msgstr ""
@@ -5291,11 +5353,6 @@ msgstr ""
msgid "unable to parse %s, error was: %s, skipping upgrade for %s"
msgstr ""
#: ./pkg/recipe/compose.go:153
#, c-format
msgid "unable to parse %s, skipping"
msgstr ""
#: ./cli/app/list.go:167
#, c-format
msgid "unable to parse %s, skipping as upgrade option"
@@ -5311,6 +5368,11 @@ msgstr ""
msgid "unable to parse deploy version: %s"
msgstr ""
#: ./pkg/recipe/compose.go:305
#, c-format
msgid "unable to parse image %s: %s"
msgstr ""
#: ./cli/run.go:176
#, c-format
msgid "unable to proceed, %s does not exist?"
@@ -5518,7 +5580,7 @@ msgstr ""
msgid "updating %s"
msgstr ""
#: ./pkg/recipe/compose.go:171 ./pkg/recipe/compose.go:237
#: ./pkg/recipe/compose.go:203 ./pkg/recipe/compose.go:378
#, c-format
msgid "updating %s to %s in %s"
msgstr ""
@@ -5543,17 +5605,17 @@ msgstr ""
msgid "upgrade [flags]"
msgstr ""
#: ./cli/recipe/upgrade.go:248
#: ./cli/recipe/upgrade.go:254
#, c-format
msgid "upgrade to which tag? (service: %s, image: %s, tag: %s)"
msgstr ""
#: ./cli/recipe/upgrade.go:254
#: ./cli/recipe/upgrade.go:260
#, c-format
msgid "upgrade to which tag? (service: %s, tag: %s)"
msgstr ""
#: ./cli/recipe/upgrade.go:221
#: ./cli/recipe/upgrade.go:224
#, c-format
msgid "upgrading service %s from %s to %s (pinned tag: %s)"
msgstr ""
@@ -5562,6 +5624,10 @@ msgstr ""
msgid "upload your recipe to git.coopcloud.tech/coop-cloud/..."
msgstr ""
#: ./pkg/lint/recipe.go:132
msgid "use a digest for all images"
msgstr ""
#: ./pkg/lint/recipe.go:83
msgid "use a tag for all images"
msgstr ""
@@ -5612,7 +5678,7 @@ msgstr ""
msgid "validated %s as server argument"
msgstr ""
#: ./pkg/lint/recipe.go:170
#: ./pkg/lint/recipe.go:177
msgid "vendor config versions in an abra.sh"
msgstr ""
@@ -5763,7 +5829,7 @@ msgstr ""
msgid "waiting for %d task(s) to reach terminal state"
msgstr ""
#: ./pkg/lint/recipe.go:21 ./pkg/lint/recipe.go:67 ./pkg/lint/recipe.go:74 ./pkg/lint/recipe.go:81 ./pkg/lint/recipe.go:88 ./pkg/lint/recipe.go:95 ./pkg/lint/recipe.go:102 ./pkg/lint/recipe.go:109 ./pkg/lint/recipe.go:116 ./pkg/lint/recipe.go:123
#: ./pkg/lint/recipe.go:21 ./pkg/lint/recipe.go:67 ./pkg/lint/recipe.go:74 ./pkg/lint/recipe.go:81 ./pkg/lint/recipe.go:88 ./pkg/lint/recipe.go:95 ./pkg/lint/recipe.go:102 ./pkg/lint/recipe.go:109 ./pkg/lint/recipe.go:116 ./pkg/lint/recipe.go:123 ./pkg/lint/recipe.go:130
msgid "warn"
msgstr ""
@@ -5806,11 +5872,11 @@ msgstr ""
msgid "writing recipe version failed: %s"
msgstr ""
#: ./cli/recipe/release.go:632 ./cli/recipe/upgrade.go:376
#: ./cli/recipe/release.go:632 ./cli/recipe/upgrade.go:412
msgid "x"
msgstr ""
#: ./cli/recipe/release.go:640 ./cli/recipe/upgrade.go:384
#: ./cli/recipe/release.go:640 ./cli/recipe/upgrade.go:420
msgid "y"
msgstr ""
@@ -5822,7 +5888,7 @@ msgstr ""
msgid "you can only use one version flag: --major, --minor or --patch"
msgstr ""
#: ./cli/recipe/release.go:648 ./cli/recipe/upgrade.go:392
#: ./cli/recipe/release.go:648 ./cli/recipe/upgrade.go:428
msgid "z"
msgstr ""
Binary file not shown.
+672 -311
View File
File diff suppressed because it is too large Load Diff
+29
View File
@@ -125,6 +125,13 @@ var LintRules = map[string][]LintRule{
HowToResolve: i18n.G("reduce length of secret names to 12 chars"),
Function: LintSecretLengths,
},
{
Ref: "R016",
Level: i18n.G("warn"),
Description: i18n.G("all images use a digest"),
HowToResolve: i18n.G("use a digest for all images"),
Function: LintAllImagesTaggedWithDigest,
},
},
"error": {
{
@@ -333,6 +340,28 @@ func LintAllImagesTagged(recipe recipe.Recipe) (bool, error) {
return true, nil
}
func LintAllImagesTaggedWithDigest(recipe recipe.Recipe) (bool, error) {
config, err := recipe.GetComposeConfig(nil)
if err != nil {
return false, err
}
for _, service := range config.Services {
img, err := reference.ParseNormalizedNamed(service.Image)
if err != nil {
return false, err
}
if reference.IsNameOnly(img) {
return false, nil
}
_, ok := img.(reference.Digested)
if !ok {
return false, nil
}
}
return true, nil
}
func LintNoUnstableTags(recipe recipe.Recipe) (bool, error) {
config, err := recipe.GetComposeConfig(nil)
if err != nil {
+82
View File
@@ -0,0 +1,82 @@
package recipe
import (
"encoding/json"
"strings"
"testing"
"coopcloud.tech/abra/pkg/config"
gitPkg "coopcloud.tech/abra/pkg/git"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
)
// Use the test recipe at the given commit, in a temporary recipes directory.
func setupTestRecipe(t *testing.T, commit string) Recipe {
t.Helper()
// Other tests in this package use the real RECIPES_DIR.
recipesDir := config.RECIPES_DIR
config.RECIPES_DIR = t.TempDir()
t.Cleanup(func() { config.RECIPES_DIR = recipesDir })
r := Get("abra-test-recipe")
if err := gitPkg.Clone(r.Dir, "https://git.coopcloud.tech/toolshed/abra-test-recipe"); err != nil {
t.Fatal(err)
}
if _, err := r.EnsureVersion(commit); err != nil {
t.Fatal(err)
}
return r
}
func TestGetRecipeVersionsIncludesDigest(t *testing.T) {
if testing.Short() {
t.Skip("skipping e2e test in short mode")
}
// https://git.coopcloud.tech/toolshed/abra-test-recipe/commit/4f0642719d2e2a568f4350cf7eea182438d58be3
r := setupTestRecipe(t, "4f0642719d2e2a568f4350cf7eea182438d58be3")
repo, err := git.PlainOpen(r.Dir)
if err != nil {
t.Fatal(err)
}
for version, commit := range map[string]string{
"9.0.0+1.21.5": "4f0642719d2e2a568f4350cf7eea182438d58be3",
"9.0.1+1.21.6": "77d752b3c61a71bfb2c45f40ec07c9d626a0e580",
} {
if _, err := repo.CreateTag(version, plumbing.NewHash(commit), nil); err != nil {
t.Fatal(err)
}
}
versions, _, err := r.GetRecipeVersions()
if err != nil {
t.Fatal(err)
}
found := map[string]ServiceMeta{}
for _, version := range versions {
for tag, services := range version {
if strings.HasPrefix(tag, "9.0.") {
found[tag] = services["app"]
}
}
}
pinned := found["9.0.0+1.21.5"]
if pinned.Tag != "1.21.5" || pinned.Digest != "sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31" {
t.Fatalf("got pinned metadata %+v", pinned)
}
unpinned := found["9.0.1+1.21.6"]
if unpinned.Tag != "1.21.6" || unpinned.Digest != "" {
t.Fatalf("got unpinned metadata %+v", unpinned)
}
for _, tc := range []struct {
meta ServiceMeta
wantDigest bool
}{{pinned, true}, {unpinned, false}} {
encoded, err := json.Marshal(tc.meta)
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(encoded), `"digest"`) != tc.wantDigest {
t.Fatalf("unexpected JSON %s", encoded)
}
}
}
+42 -13
View File
@@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"coopcloud.tech/abra/pkg/formatter"
@@ -110,10 +111,23 @@ func (r Recipe) GetVersionLabelLocal() (string, error) {
return label, nil
}
var sha256Digest = regexp.MustCompile(`^sha256:[a-f0-9]{64}$`)
// UpdateTag updates an image tag in-place on file system local compose files.
func (r Recipe) UpdateTag(image, tag string) (bool, error) {
// A non-empty digest pins the tag (image:tag@sha256:...).
func (r Recipe) UpdateTag(image, tag, digest string) (bool, error) {
// Optional parameter, but must be a valid sha256 digest if provided.
if digest != "" && !sha256Digest.MatchString(digest) {
return false, errors.New(i18n.G("invalid digest %s, want sha256:<64 hex digits>", digest))
}
fullPattern := fmt.Sprintf("%s/compose**yml", r.Dir)
image = formatter.StripTagMeta(image)
source, parseErr := reference.ParseNormalizedNamed(image)
_, exactImage := source.(reference.NamedTagged)
if parseErr != nil {
return false, parseErr
}
imageName := formatter.StripTagMeta(reference.Path(source))
composeFiles, err := filepath.Glob(fullPattern)
if err != nil {
@@ -122,6 +136,7 @@ func (r Recipe) UpdateTag(image, tag string) (bool, error) {
log.Debug(i18n.G("considering %s config(s) for tag update", strings.Join(composeFiles, ", ")))
updated := false
for _, composeFile := range composeFiles {
opts := stack.Deploy{Composefiles: []string{composeFile}}
@@ -140,44 +155,58 @@ func (r Recipe) UpdateTag(image, tag string) (bool, error) {
continue // may be a compose.$optional.yml file
}
img, _ := reference.ParseNormalizedNamed(service.Image)
img, err := reference.ParseNormalizedNamed(formatter.RemoveSha(service.Image))
if err != nil {
return false, err
}
var composeTag string
switch img.(type) {
case reference.NamedTagged:
composeTag = img.(reference.NamedTagged).Tag()
default:
log.Debug(i18n.G("unable to parse %s, skipping", img))
continue
}
// Found image that needs replacing
composeImage := formatter.StripTagMeta(reference.Path(img))
log.Debug(i18n.G("parsed %s from %s", composeTag, service.Image))
if image == composeImage {
if imageName == composeImage && (!exactImage || service.Image == image) {
bytes, err := ioutil.ReadFile(composeFile)
if err != nil {
return false, err
}
old := fmt.Sprintf("%s:%s", composeImage, composeTag)
new := fmt.Sprintf("%s:%s", composeImage, tag)
replacedBytes := strings.Replace(string(bytes), old, new, -1)
old := service.Image
tagged, err := reference.WithTag(reference.TrimNamed(img), tag)
if err != nil {
return false, err
}
new := reference.FamiliarString(tagged)
if digest != "" {
new += "@" + digest
}
if old == new {
continue
}
// Don't confuse digests if the image isn't an exact match. E.g. if old is postgres:13, don't
// match postgres:13-alpine
pattern := regexp.MustCompile(`(?m)(^\s*image:\s*["']?)` + regexp.QuoteMeta(old) + `(["']?\s*(?:#.*)?$)`)
replacedBytes := pattern.ReplaceAllString(string(bytes), "${1}"+new+"${2}")
if replacedBytes == string(bytes) {
continue
}
log.Debug(i18n.G("updating %s to %s in %s", old, new, compose.Filename))
if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil {
return false, err
}
updated = true
}
}
}
return false, nil
return updated, nil
}
// UpdateLabel updates a label in-place on file system local compose files.
+7 -6
View File
@@ -164,7 +164,7 @@ func (r Recipe) EnsureVersion(version string) (bool, error) {
hash, err := repo.ResolveRevision(plumbing.Revision(version))
if err != nil {
log.Fatal(i18n.G("unable to resolve '%s': %s", version, err))
return isChaosCommit, errors.New(i18n.G("unable to resolve '%s': %s", version, err))
}
opts = &git.CheckoutOptions{Hash: *hash, Create: false, Force: true}
@@ -175,11 +175,11 @@ func (r Recipe) EnsureVersion(version string) (bool, error) {
worktree, err := repo.Worktree()
if err != nil {
return isChaosCommit, nil
return isChaosCommit, err
}
if err := worktree.Checkout(opts); err != nil {
return isChaosCommit, nil
return isChaosCommit, err
}
log.Debug(i18n.G("successfully checked %s out to %s in %s", r.Name, tagRef.Short(), r.Dir))
@@ -452,10 +452,11 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
continue
}
versionMeta[service.Name] = ServiceMeta{
Image: path,
Tag: tag,
meta := ServiceMeta{Image: path, Tag: tag}
if pinned, ok := img.(reference.Digested); ok {
meta.Digest = pinned.Digest().String()
}
versionMeta[service.Name] = meta
}
versions = append(versions, map[string]map[string]ServiceMeta{tag: versionMeta})
+3 -2
View File
@@ -39,8 +39,9 @@ type service = string
// ServiceMeta represents meta info associated with a service.
type ServiceMeta struct {
Image string `json:"image"`
Tag string `json:"tag"`
Image string `json:"image"`
Tag string `json:"tag"`
Digest string `json:"digest,omitempty"`
}
// RecipeVersions are the versions associated with a recipe.
+2 -1
View File
@@ -7,4 +7,5 @@
* Integration tests are in `./tests/integration`. Please see [these
docs](https://docs.coopcloud.tech/abra/hack/#integration-tests) for
instructions and tips on how to run them.
instructions and tips on how to run them. If you want to run it locally via nix,
read the [nix testing docs](../nix/hosts/README.md).
+73 -5
View File
@@ -598,8 +598,7 @@ teardown(){
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
_inspect_env_test_app
assert_success
assert_output --partial "WITH_COMMENT=foo"
@@ -610,8 +609,7 @@ teardown(){
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --force
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
_inspect_env_test_app
assert_success
refute_output --partial "WITH_COMMENT=foo"
assert_output --partial "WITH_COMMENT=bar"
@@ -629,8 +627,14 @@ teardown(){
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
assert_success
run docker service inspect --format '{{ range .Endpoint.Ports }}{{ .Protocol }}={{ .PublishedPort }}{{ end }}' \
if _is_local;
then
run docker service inspect --format "{{ range .Endpoint.Ports }}{{ .Protocol }}={{ .PublishedPort }}{{ end }}" \
"${TEST_APP_DOMAIN//./_}_app"
else
run ssh "$TEST_SERVER" "docker service inspect --format '{{ range .Endpoint.Ports }}{{ .Protocol }}={{ .PublishedPort }}{{ end }}' ${TEST_APP_DOMAIN//./_}_app"
fi
assert_success
assert_output --partial "tcp=1312"
assert_output --partial "udp=1312"
@@ -702,3 +706,67 @@ teardown(){
assert_failure
assert_output --partial "unexpected character"
}
# Deploy pinned 1.21.5, upgrade to pinned 1.21.6, then roll back to 1.21.5.
# bats test_tags=slow
@test "deploy, upgrade and rollback digest-pinned images" {
recipeDir="$ABRA_DIR/recipes/$TEST_RECIPE"
service="${TEST_APP_DOMAIN//./_}_app"
# Manually upgrade the unpinned 0.3.5 recipe to 1.21.5 and pin it.
run git -C "$recipeDir" checkout "0.3.5+1.21.0"
assert_success
run docker pull nginx:1.21.5
assert_success
run docker image inspect --format '{{index .RepoDigests 0}}' nginx:1.21.5
assert_success
oldImage="nginx:1.21.5@${output#*@}"
run sed -i "s|image: nginx:1.21.0|image: $oldImage|" "$recipeDir/compose.yml"
assert_success
# Label and tag the local recipe release so app commands can track it.
run sed -i 's/version=0.3.5+1.21.0/version=0.4.0+1.21.5/' "$recipeDir/compose.yml"
assert_success
_git_commit
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag \
-a "0.4.0+1.21.5" -m "fake: 0.4.0+1.21.5"
assert_success
# Manually upgrade and pin the next release to 1.21.6.
run docker pull nginx:1.21.6
assert_success
run docker image inspect --format '{{index .RepoDigests 0}}' nginx:1.21.6
assert_success
newImage="nginx:1.21.6@${output#*@}"
run sed -i "s|image: $oldImage|image: $newImage|" "$recipeDir/compose.yml"
assert_success
# Update the release label to match the next recipe tag.
run sed -i 's/version=0.4.0+1.21.5/version=0.4.1+1.21.6/' "$recipeDir/compose.yml"
assert_success
_git_commit
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag \
-a "0.4.1+1.21.6" -m "fake: 0.4.1+1.21.6"
assert_success
# Deploy recipe 0.4.0 with the exact pinned 1.21.5 image.
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.4.0+1.21.5" --offline --no-input --no-converge-checks
assert_success
run docker service inspect --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}' "$service"
assert_success
assert_output "$oldImage"
# Upgrade to recipe 0.4.1 and the exact pinned 1.21.6 image.
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.4.1+1.21.6" --offline --no-input --no-converge-checks
assert_success
run docker service inspect --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}' "$service"
assert_success
assert_output "$newImage"
# Roll back to recipe 0.4.0 and its original pinned image.
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.4.0+1.21.5" --offline --no-input --no-converge-checks
assert_success
run docker service inspect --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}' "$service"
assert_success
assert_output "$oldImage"
}
@@ -133,8 +133,7 @@ teardown(){
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
_inspect_env_test_app
assert_success
assert_output --partial "$TEST_RECIIPE:0.1.0+1.20.0"
}
+11 -4
View File
@@ -140,15 +140,22 @@ teardown(){
_undeploy_app
sanitisedDomainName="${TEST_APP_DOMAIN//./_}"
run docker config create "${sanitisedDomainName}_test_conf_v99" "$ABRA_DIR/recipes/abra-test-recipe/abra.sh"
assert_success
remote_ssh_command=""
if _is_local;
then
run docker config create "${sanitisedDomainName}_test_conf_v99" "$ABRA_DIR/recipes/abra-test-recipe/abra.sh"
else
remote_ssh_command="ssh '$TEST_SERVER'"
run cat "$ABRA_DIR/recipes/abra-test-recipe/abra.sh" | ssh "$TEST_SERVER" docker config create "${sanitisedDomainName}_test_conf_v99 -"
assert_success
fi
assert bash -c "docker config ls | grep -q test_conf_v99"
assert bash -c "$remote_ssh_command docker config ls | grep -q test_conf_v99"
run $ABRA app rm "$TEST_APP_DOMAIN" --no-input
assert_success
refute bash -c "docker config ls | grep -q test_conf_v99"
refute bash -c "$remote_ssh_command docker config ls | grep -q test_conf_v99"
}
@test "remove .env file" {
@@ -54,8 +54,7 @@ teardown(){
--no-input
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
_inspect_env_test_app
assert_success
assert_output --partial "$TEST_RECIIPE:0.1.0+1.20.0"
}
@@ -53,8 +53,7 @@ teardown(){
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
_inspect_env_test_app
assert_success
assert_output --partial "$TEST_RECIIPE:0.2.0+1.21.0"
assert_output --partial "$TEST_RECIPE:0.2.0+1.21.0"
}
+11 -2
View File
@@ -31,9 +31,18 @@ _ensure_ssh_agent() {
exit 1
fi
export SSH_AUTH_SOCK="$HOME/.ssh/ssh_auth_sock"
if [ ! -S ~/.ssh/ssh_auth_sock ]; then
if [[ ! -v SSH_AUTH_SOCK ]]; then
export SSH_AUTH_SOCK="$HOME/.ssh/ssh_auth_sock"
eval `ssh-agent`
ln -sf "$SSH_AUTH_SOCK" ~/.ssh/ssh_auth_sock
fi
}
_is_local() {
if [[ "$TEST_SERVER" == "default" ]];
then
return 0
else
return 1
fi
}
+30 -6
View File
@@ -1,13 +1,37 @@
#!/usr/bin/env bash
_ensure_swarm() {
if [ "$(docker info | grep Swarm | sed 's/Swarm: //g' | tr -d ' ')" == "inactive" ]; then
run docker swarm init --advertise-addr 127.0.0.1:2377
assert_success
if [ "$(docker info | grep Swarm | sed 's/Swarm: //g' | tr -d ' ')" == "inactive" ]; then
run docker swarm init --advertise-addr 127.0.0.1:2377
assert_success
fi
if ! $(docker network ls | grep -q 'proxy'); then
run docker network create -d overlay proxy
assert_success
if ! docker network ls | grep -q 'proxy'; then
run docker network create -d overlay proxy
assert_success
fi
if ! _is_local;
then
if [ "$(ssh "$TEST_SERVER" docker info | grep Swarm | sed 's/Swarm: //g' | tr -d ' ')" == "inactive" ]; then
run ssh "$TEST_SERVER" docker swarm init --advertise-addr 127.0.0.1:2377
assert_success
fi
if ! ssh "$TEST_SERVER" docker network ls | grep -q 'proxy'; then
run ssh "$TEST_SERVER" docker network create -d overlay proxy
assert_success
fi
fi
}
_inspect_env_test_app() {
if _is_local;
then
containerId="$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)"
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' "$containerId"
else
containerId="$(ssh "$TEST_SERVER" docker ps -f name="$TEST_APP_DOMAIN" -q)"
run ssh "$TEST_SERVER" "docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' '$containerId'"
fi
}
-2
View File
@@ -38,8 +38,6 @@ teardown(){
run $ABRA recipe upgrade "custom-html" --no-input
assert_success
assert_output --partial 'can upgrade service: app'
assert_exists "$ABRA_DIR/recipes/custom-html"
}
+3 -3
View File
@@ -27,7 +27,7 @@ teardown(){
assert bash -c "docker context ls | grep -q $TEST_SERVER"
server_dir_perms=$(stat -c "%a" "$ABRA_DIR/servers/$TEST_SERVER")
assert_equal $server_dir_perms "700"
assert_equal "$server_dir_perms" "700"
}
@test "error if using name and --local together" {
@@ -43,8 +43,8 @@ teardown(){
assert bash -c "docker context ls | grep -q default"
assert_output --partial 'local server successfully added'
server_dir_perms=$(stat -c "%a" "$ABRA_DIR/servers/$TEST_SERVER")
assert_equal $server_dir_perms "700"
server_dir_perms=$(stat -c "%a" "$ABRA_DIR/servers/default")
assert_equal "$server_dir_perms" "700"
}
@test "create local server fails when no docker swarm" {
+4
View File
@@ -329,6 +329,10 @@ func (c *Config) unmarshalCore() {
if parsed := parseConfigBool(s.Options.Get(protectHFSKey)); parsed.IsSet() {
c.Core.ProtectHFS = parsed
}
if s.Options.Get(repositoryFormatVersionKey) == string(format.Version_1) {
c.Core.RepositoryFormatVersion = format.Version_1
}
}
func (c *Config) unmarshalUser() {
@@ -139,8 +139,12 @@ func (dw *deltaSelector) fixAndBreakChains(objectsToPack []*ObjectToPack) error
m[otp.Hash()] = otp
}
// visiting holds the objects on the current resolution path, so that a
// delta chain looping back on itself can be detected and broken.
visiting := make(map[plumbing.Hash]bool)
for _, otp := range objectsToPack {
if err := dw.fixAndBreakChainsOne(m, otp); err != nil {
if err := dw.fixAndBreakChainsOne(m, otp, visiting); err != nil {
return err
}
}
@@ -148,7 +152,11 @@ func (dw *deltaSelector) fixAndBreakChains(objectsToPack []*ObjectToPack) error
return nil
}
func (dw *deltaSelector) fixAndBreakChainsOne(objectsToPack map[plumbing.Hash]*ObjectToPack, otp *ObjectToPack) error {
func (dw *deltaSelector) fixAndBreakChainsOne(
objectsToPack map[plumbing.Hash]*ObjectToPack,
otp *ObjectToPack,
visiting map[plumbing.Hash]bool,
) error {
if !otp.Object.Type().IsDelta() {
return nil
}
@@ -174,7 +182,21 @@ func (dw *deltaSelector) fixAndBreakChainsOne(objectsToPack map[plumbing.Hash]*O
return dw.undeltify(otp)
}
if err := dw.fixAndBreakChainsOne(objectsToPack, base); err != nil {
// Mark this object as being resolved before looking at its base, so that
// a delta based on itself is caught by the check below.
h := otp.Hash()
visiting[h] = true
defer delete(visiting, h)
// A delta chain that loops back onto an object we are already resolving
// cannot be written: every delta needs its base written first. Break the
// chain here instead of following the cycle, which would recurse until
// the goroutine stack is exhausted.
if visiting[do.BaseHash()] {
return dw.undeltify(otp)
}
if err := dw.fixAndBreakChainsOne(objectsToPack, base, visiting); err != nil {
return err
}
+34 -2
View File
@@ -474,6 +474,14 @@ type TreeWalker struct {
recursive bool
seen map[plumbing.Hash]bool
// skipPathValidation disables the pathutil.ValidTreePath check in Next.
// It is set by inspection-only callers (e.g. the revlist object walk)
// that never funnel entry names into the filesystem and must enumerate
// trees faithfully, including entries with names upstream Git accepts
// but that are unsafe to materialise (control characters, `.git`-shaped
// names).
skipPathValidation bool
s storer.EncodedObjectStorer
t *Tree
}
@@ -496,10 +504,32 @@ func NewTreeWalker(t *Tree, recursive bool, seen map[plumbing.Hash]bool) *TreeWa
}
}
// SkipPathValidation disables the pathutil.ValidTreePath check performed by
// Next, and must be called before the first Next.
//
// It is for inspection-only walks that never funnel an entry name into the
// filesystem — enumerating which objects exist, rather than materialising
// them — and that must therefore see the tree faithfully, including entries
// whose names upstream Git accepts but that are unsafe to check out. Callers
// that hand the returned name to filesystem or archive output must not use
// it; path safety for those is enforced at the materialisation boundaries
// (FindEntry, TreeEntryFile, archive, FileIter).
func (w *TreeWalker) SkipPathValidation() {
w.skipPathValidation = true
}
// Next returns the next object from the tree. Objects are returned in order
// and subtrees are included. After the last object has been returned further
// calls to Next() will return io.EOF.
//
// Each entry's name is validated against pathutil.ValidTreePath as it
// surfaces, so callers that funnel the returned name into filesystem
// or archive output can trust it is free of `.git`-shaped components,
// HFS+/NTFS variants, Windows reserved names, and traversal sequences.
// A malformed entry stops the walk with the validator's error;
// inspection-only callers that need to enumerate raw, unvalidated
// names can read Tree.Entries directly or call SkipPathValidation.
//
// In the current implementation any objects which cannot be found in the
// underlying repository will be skipped automatically. It is possible that this
// may change in future versions.
@@ -536,8 +566,10 @@ func (w *TreeWalker) Next() (name string, entry TreeEntry, err error) {
continue
}
if err := pathutil.ValidTreePath(entry.Name); err != nil {
return name, entry, err
if !w.skipPathValidation {
if err := pathutil.ValidTreePath(entry.Name); err != nil {
return name, entry, err
}
}
if entry.Mode == filemode.Dir {
+6
View File
@@ -106,6 +106,12 @@ func transformChildren(t *Tree) ([]noder.Noder, error) {
ret := make([]noder.Noder, 0, len(t.Entries))
walker := NewTreeWalker(t, false, nil) // don't recurse
// The diff walk is read-only and never materialises entry names into the
// filesystem, so it must enumerate the tree faithfully — including entries
// with names that are unsafe to check out but valid per upstream Git (e.g.
// control characters). Path safety is enforced at materialisation
// boundaries (FindEntry, TreeEntryFile, archive, FileIter), not here.
walker.SkipPathValidation()
// don't defer walker.Close() for efficiency reasons.
for {
_, e, err = walker.Next()
+7
View File
@@ -187,6 +187,13 @@ func iterateCommitTrees(
cb(tree.Hash)
treeWalker := object.NewTreeWalker(tree, true, seen)
// This walk only enumerates which objects are reachable, to decide what
// to send; it never materialises an entry name into the filesystem. It
// must therefore enumerate the tree faithfully, including entries with
// names that are unsafe to check out but valid per upstream Git (e.g.
// control characters). Path safety is enforced at materialisation
// boundaries (FindEntry, TreeEntryFile, archive, FileIter), not here.
treeWalker.SkipPathValidation()
for {
_, e, err := treeWalker.Next()
+379 -22
View File
@@ -6,8 +6,12 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/netip"
"net/url"
"reflect"
"strconv"
@@ -73,7 +77,7 @@ func advertisedReferences(ctx context.Context, s *session, serviceName string) (
s.endpoint.String(), infoRefsPath, serviceName,
)
req, err := http.NewRequest(http.MethodGet, url, nil)
req, err := newRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
@@ -177,6 +181,24 @@ func NewClient(c *http.Client) transport.Transport {
// and other custom options specific to the client.
// If the net/http client is nil or empty, it will use a net/http client configured
// with http.DefaultTransport.
//
// Credentials this client adds where the transport cannot see them are not
// subject to the redirect stripping described on AuthMethod: a RoundTripper
// injects after the hop is decided, and Client.Jar is consulted after
// CheckRedirect, so a domain cookie still follows a redirect to a subdomain
// the transport counts as another origin. Apply them in an AuthMethod instead
// if that is not wanted. A CheckRedirect hook set on this client runs
// alongside the transport's own, but any header it adds when a redirect
// leaves the repository's origin is discarded the same way.
//
// A RoundTripper is therefore also how to authenticate to a new origin a
// redirect has moved the repository to: match on the request URL and inject
// the credential only for that origin, so it is not sent anywhere else. The
// transport keeps its own CheckRedirect on the copy it makes of this client,
// so the policy and the origin checks still apply.
//
// None of this applies to a RoundTripper that follows redirects itself:
// CheckRedirect is not consulted then, so no stripping happens at all.
func NewClientWithOptions(c *http.Client, opts *ClientOptions) transport.Transport {
if c == nil {
c = &http.Client{
@@ -370,11 +392,20 @@ func (s *session) ModifyEndpointIfRedirect(res *http.Response) error {
if !strings.HasSuffix(r.URL.Path, infoRefsPath) {
return fmt.Errorf("http redirect: target %q does not end with %s", r.URL.Path, infoRefsPath)
}
if r.URL.Scheme != "http" && r.URL.Scheme != "https" {
// A scheme is case-insensitive per RFC 3986, and checkRedirect folds case
// when it reads the same hop, so fold here too rather than reject a
// spelling that check let through. url.Parse and transport.NewEndpoint
// both lowercase what they parse, so only a hand-built URL or Endpoint
// arrives uppercased. The folded form is what gets stored below, so every
// later request built from the endpoint carries the canonical spelling.
scheme := strings.ToLower(r.URL.Scheme)
if scheme != "http" && scheme != "https" {
return fmt.Errorf("http redirect: unsupported scheme %q", r.URL.Scheme)
}
if r.URL.Scheme != s.endpoint.Protocol &&
!(s.endpoint.Protocol == "http" && r.URL.Scheme == "https") {
// schemeUpgrade rather than an inline comparison, so the one cross-scheme
// change go-git permits has a single definition shared with
// credentialsMayFollow.
if !strings.EqualFold(scheme, s.endpoint.Protocol) && !schemeUpgrade(s.endpoint.Protocol, scheme) {
return fmt.Errorf("http redirect: changes scheme from %q to %q", s.endpoint.Protocol, r.URL.Scheme)
}
@@ -384,7 +415,20 @@ func (s *session) ModifyEndpointIfRedirect(res *http.Response) error {
return err
}
if host != s.endpoint.Host || effectivePort(r.URL.Scheme, port) != effectivePort(s.endpoint.Protocol, s.endpoint.Port) {
// The session stores the endpoint and re-applies its credentials on every
// later request, so clear them once the redirect has left the origin they
// were issued for. This uses the same predicate as stripCredentials, so
// both halves share one definition of an origin.
//
// The two are deliberately asymmetric in one respect: stripCredentials is
// sticky over the whole chain, so an origin -> evil -> origin redirect
// leaves the discovery GET's later hops unauthenticated even though the
// chain returned home. This compares the endpoint only against the final
// URL, so the same round trip leaves the session authenticated. That is
// not a leak - the final URL's origin is the original one - but it means
// such a chain can make the discovery GET anonymous while the session's
// POSTs are authenticated, which can surface as a confusing 401.
if !credentialsMayFollow(endpointURL(s.endpoint), r.URL) {
s.endpoint.User = ""
s.endpoint.Password = ""
s.auth = nil
@@ -393,7 +437,7 @@ func (s *session) ModifyEndpointIfRedirect(res *http.Response) error {
s.endpoint.Host = host
s.endpoint.Port = port
s.endpoint.Protocol = r.URL.Scheme
s.endpoint.Protocol = scheme
s.endpoint.Path = r.URL.Path[:len(r.URL.Path)-len(infoRefsPath)]
return nil
}
@@ -419,19 +463,132 @@ func endpointPort(port string) (int, error) {
return parsed, nil
}
func effectivePort(scheme string, port int) int {
if port != 0 {
return port
}
// schemeUpgrade reports whether the scheme transition from one URL to another
// is the one cross-scheme change go-git permits: a plain-http origin upgrading
// to https. It strictly improves confidentiality and is how servers steer
// clients off cleartext.
//
// Permitting it at all is a deliberate deviation: curl, git and the Fetch
// standard all count scheme as part of host identity and drop credentials on
// the upgrade. Auth is sent pre-emptively here, so an http origin has already
// spent its credential in cleartext on the first request and refusing the
// upgrade would break the clone without unspending it. The host is unchanged,
// where an on-path attacker needs a valid certificate to receive anything.
func schemeUpgrade(from, to string) bool {
return strings.EqualFold(from, "http") && strings.EqualFold(to, "https")
}
switch strings.ToLower(scheme) {
case "http":
return 80
case "https":
return 443
default:
return 0
// canonicalHost returns u's hostname in the form origins are compared in.
//
// An address literal is normalised by netip, so the many spellings of one
// address are one origin. Two literals are the same origin exactly when netip
// parses them to the same Addr, which is also how the WHATWG URL Standard
// compares hosts. An IPv4-mapped literal is deliberately not unmapped onto
// the IPv4 it dials: reaching the same endpoint is not the same authority,
// since net/http sends the literal as written in Host and a server may route
// the two spellings to different virtual hosts.
//
// netip also keeps a scope zone verbatim, which is what origin comparison
// needs: net resolves a zone to an interface by exact name, so folding %eth0
// onto %ETH0 would call two hosts the same origin that net dials down
// different interfaces.
//
// A registered name is ASCII-lowercased. That fold is the only liberty taken;
// every other difference in spelling is a different origin.
//
// A trailing root dot is one such difference and is kept, for the same reason
// as the IPv4-mapped literal: curl and the WHATWG URL Standard both hold
// "example.com." and "example.com" to be distinct hosts, and although
// crypto/tls and crypto/x509 fold the dot when they authenticate the peer,
// net/http sends the name as written in Host.
//
// The fold is deliberately ASCII-only. strings.ToLower and strings.EqualFold
// apply Unicode case mapping, which folds U+03C2 onto U+03C3 and so would
// call two hosts the same origin when they resolve to different servers. An
// ASCII-only fold cannot merge two names DNS keeps apart.
//
// No IDNA mapping is applied either, so a unicode hostname is a different
// origin from the punycode encoding of it, and from another Unicode case of
// itself, even though all three reach the same server. Mapping through
// golang.org/x/net/idna would join them, but it can only widen this equality,
// never narrow it, so leaving it out can cost a credential across such a
// redirect and cannot forward one. Against that cost, go-git pins x/net while
// net/http uses the copy vendored into the toolchain: the two are versioned
// separately, so a release that moves the Unicode tables under one and not
// the other would have this merge origins net/http still dials apart. That is
// the failure this comparison exists to prevent, and comparing bytes has no
// such mode.
func canonicalHost(u *url.URL) string {
host := u.Hostname()
if addr, err := netip.ParseAddr(host); err == nil {
return addr.String()
}
b := []byte(host)
for i := range b {
if b[i] >= 'A' && b[i] <= 'Z' {
b[i] += 'a' - 'A'
}
}
return string(b)
}
// effectivePort returns u's port as the connection will use it: the scheme's
// well-known port when the URL does not spell one out, and without leading
// zeroes, so "https://x", "https://x:443" and "https://x:0443" all agree.
func effectivePort(u *url.URL) string {
port := u.Port()
if port == "" {
switch strings.ToLower(u.Scheme) {
case "http":
return "80"
case "https":
return "443"
default:
return ""
}
}
if trimmed := strings.TrimLeft(port, "0"); trimmed != "" {
return trimmed
}
return "0"
}
// credentialsMayFollow reports whether credentials issued for one URL may be
// sent to another.
//
// The relation is deliberately asymmetric: scheme, host and effective port
// must all match, except that a plain http origin may upgrade to https on the
// same host (see schemeUpgrade). That exception is confined to the two
// default ports: 80 to 443 is the upgrade servers actually steer clients
// through, whereas a non-default port carries no such convention, so
// http://host:8080 to https://host:8443 is a move to another origin like any
// other port change.
//
// Host matching is exact. Unlike Go's http.Client, which forwards credentials
// from a host to any subdomain of it, a subdomain is a different origin here —
// matching canonical git and libcurl.
func credentialsMayFollow(from, to *url.URL) bool {
if canonicalHost(from) != canonicalHost(to) {
return false
}
if strings.EqualFold(from.Scheme, to.Scheme) {
return effectivePort(from) == effectivePort(to)
}
return schemeUpgrade(from.Scheme, to.Scheme) &&
effectivePort(from) == "80" && effectivePort(to) == "443"
}
// endpointURL renders an Endpoint's origin as a URL, so that the session's
// credential clearing and the per-hop stripping share one definition of an
// origin and cannot drift apart.
func endpointURL(ep *transport.Endpoint) *url.URL {
host := strings.Trim(ep.Host, "[]")
if ep.Port != 0 {
host = net.JoinHostPort(host, strconv.Itoa(ep.Port))
} else if strings.Contains(host, ":") {
host = "[" + host + "]"
}
return &url.URL{Scheme: ep.Protocol, Host: host}
}
func (c *client) cloneHTTPClient(transport http.RoundTripper) *http.Client {
@@ -448,26 +605,215 @@ func wrapCheckRedirect(policy RedirectPolicy, next func(*http.Request, []*http.R
if err := checkRedirect(req, via, policy); err != nil {
return err
}
// Strip before the caller's hook so it observes what will actually
// be sent, and again afterwards so a hook of the common "preserve
// my headers across redirects" shape - which copies from via[0],
// the original unsanitized request - cannot reinstate them.
// Carrying credentials across an origin boundary is deliberately
// unsupported.
stripCredentials(req, via)
if next != nil {
return next(req, via)
if err := next(req, via); err != nil {
return err
}
}
stripCredentials(req, via)
return nil
}
}
// safeHeaders lists the headers go-git sets itself, none of which can carry a
// caller credential. stripCredentials keeps only these when a redirect leaves
// the credential's origin. Adding a name here makes it forwardable across an
// origin boundary - do not add anything a caller can put a secret in.
//
// This narrows rather than eliminates the exposure: an AuthMethod that writes
// a credential into one of these names directly - for example
// Header.Set("User-Agent", "token "+secret) - still survives a cross-origin
// redirect. Such a value is also sent to the origin and to any proxy in path,
// so it should not be placed there whether or not a redirect follows.
var safeHeaders = map[string]struct{}{
"User-Agent": {},
"Host": {},
"Accept": {},
"Content-Type": {},
"Content-Length": {},
}
func filterHeaders(h http.Header) http.Header {
filtered := make(http.Header)
for key, values := range h {
if _, ok := safeHeaders[http.CanonicalHeaderKey(key)]; ok {
filtered[key] = values
}
}
return filtered
}
// stripCredentials removes credentials from req once the redirect chain has
// left the origin of the original, credential-bearing request.
//
// CheckRedirect is the only hook that runs while a redirected request's
// headers are still mutable: http.Client.Do performs the entire chain
// internally, so anything the transport does after Do returns - including
// ModifyEndpointIfRedirect - is too late for the hops themselves.
//
// Two subtleties:
//
// - net/http rebuilds every redirect request from the original request's
// headers before calling this, so a header removed at one hop reappears
// at the next. The decision is therefore recomputed per hop.
// - The decision is sticky: once the chain has left the origin, credentials
// stay gone even if a later hop returns to it. Stickiness is derived from
// via rather than stored, because this closure is shared across a
// session's requests.
//
// Stripping keeps only the headers go-git sets itself (safeHeaders). An
// allowlist is used rather than a list of credential header names because
// caller credentials arrive under names that cannot be enumerated -
// PRIVATE-TOKEN, X-Api-Key, gateway headers - which is exactly what
// net/http's fixed list of sensitive header names gets wrong. It is also
// immune to header-name canonicalisation: an AuthMethod that writes a raw map
// key is still removed.
func stripCredentials(req *http.Request, via []*http.Request) {
if len(via) == 0 {
return
}
// net/http sets a URL on every request it builds, and req.URL is non-nil
// by construction: checkRedirect dereferences req.URL.Scheme on each path
// that returns nil, so it runs first or not at all. This nil check and
// the two in crossedOrigin are defensive, against a synthetic caller.
// Each treats a URL it cannot read as an origin crossing; removing one
// panics in canonicalHost rather than leaking.
if origin := via[0].URL; origin != nil && !crossedOrigin(origin, req, via) {
return
}
req.Header = filterHeaders(req.Header)
if req.URL != nil {
req.URL.User = nil
}
}
// crossedOrigin reports whether any hop so far, including the pending one, has
// left origin.
func crossedOrigin(origin *url.URL, req *http.Request, via []*http.Request) bool {
if req.URL == nil || !credentialsMayFollow(origin, req.URL) {
return true
}
for _, prev := range via[1:] {
if prev.URL == nil || !credentialsMayFollow(origin, prev.URL) {
return true
}
}
return false
}
// redactedURL returns the string form of u with the userinfo password
// replaced, for use in error messages. (*url.URL).String() renders the
// password verbatim, and request URLs are built from the endpoint, which
// carries whatever credentials the caller put in the clone URL.
func redactedURL(u *url.URL) string {
if u == nil {
return ""
}
if u.User == nil {
return u.String()
}
if _, hasPassword := u.User.Password(); !hasPassword {
return u.String()
}
redacted := *u
redacted.User = url.UserPassword(u.User.Username(), "REDACTED")
return redacted.String()
}
// redactedRawURL is redactedURL for a string that may not parse. Request URLs
// are assembled from Endpoint.String(), which re-emits Endpoint.Path raw, so a
// path holding a stray percent produces a string url.Parse rejects. url.Parse
// reports the input verbatim and applies no redaction of its own; only
// http.Client strips a password, and only from errors it raises itself.
func redactedRawURL(raw string) string {
i := strings.Index(raw, "://")
if i < 0 {
return raw
}
authority := raw[i+3:]
if end := strings.IndexByte(authority, '/'); end >= 0 {
authority = authority[:end]
}
at := strings.LastIndexByte(authority, '@')
if at < 0 {
return raw
}
colon := strings.IndexByte(authority[:at], ':')
if colon < 0 {
// Username only, left alone, as redactedURL leaves it.
return raw
}
return raw[:i+3+colon+1] + "REDACTED" + raw[i+3+at:]
}
// newRequest wraps http.NewRequest so that a URL it cannot parse does not
// reach the caller with the endpoint's credentials still in it.
func newRequest(method, rawURL string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequest(method, rawURL, body)
if err != nil {
var uerr *url.Error
if errors.As(err, &uerr) {
uerr.URL = redactedRawURL(uerr.URL)
}
return nil, err
}
return req, nil
}
func checkRedirect(req *http.Request, via []*http.Request, policy RedirectPolicy) error {
// CheckRedirect is the only hook that runs before the next hop leaves
// the client. ModifyEndpointIfRedirect inspects the chain after
// client.Do has followed all of it, so a hop rejected there has already
// carried the request headers to its server.
//
// The wording matches the message ModifyEndpointIfRedirect produces for
// the same hop, which this check reaches first.
if len(via) != 0 {
// A hop whose scheme cannot be read cannot be shown not to have been
// https, so it is assumed to have been, and a cleartext target is
// rejected. Skipping the comparison instead would let an
// undeterminable hop turn the check off, which is the wrong default
// for a credential control; crossedOrigin fails closed the same way.
// An empty scheme is as unreadable as a nil URL, so both take the
// assumed-https default.
//
// The comparisons fold case because a scheme is case-insensitive per
// RFC 3986. net/url lowercases what it parses, so only a hand-built
// URL reaches here uppercased - the same synthetic caller the nil
// checks guard against - and for that caller "HTTPS" to "http" is
// still a downgrade.
prevScheme := "https"
if prev := via[len(via)-1]; prev.URL != nil && prev.URL.Scheme != "" {
prevScheme = prev.URL.Scheme
}
if strings.EqualFold(prevScheme, "https") && strings.EqualFold(req.URL.Scheme, "http") {
return fmt.Errorf("http redirect: changes scheme from %q to %q: %s",
prevScheme, req.URL.Scheme, redactedURL(req.URL))
}
}
switch policy {
case FollowRedirects:
case NoFollowRedirects:
return fmt.Errorf("http redirect: redirects disabled to %s", req.URL)
return fmt.Errorf("http redirect: redirects disabled to %s", redactedURL(req.URL))
case "", FollowInitialRedirects:
if !isInitialRequest(req) {
return fmt.Errorf("http redirect: redirect on non-initial request to %s", req.URL)
return fmt.Errorf("http redirect: redirect on non-initial request to %s", redactedURL(req.URL))
}
default:
return fmt.Errorf("http redirect: invalid redirect policy %q", policy)
}
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
// Folded for the same reason as the guard above: a scheme is
// case-insensitive per RFC 3986, so a spelling the downgrade check read
// as https must not be rejected here as a scheme go-git cannot speak.
if !strings.EqualFold(req.URL.Scheme, "http") && !strings.EqualFold(req.URL.Scheme, "https") {
return fmt.Errorf("http redirect: unsupported scheme %q", req.URL.Scheme)
}
if len(via) >= 10 {
@@ -481,6 +827,17 @@ func (*session) Close() error {
}
// AuthMethod is concrete implementation of common.AuthMethod for HTTP services
//
// Headers SetAuth adds are dropped when a redirect leaves the repository's
// origin: only the headers the transport sets itself survive that boundary.
// This applies to non-credential headers too, so an implementation that adds a
// trace or tenant header loses it on such a hop.
//
// That filter matches header names, not values. An implementation that writes
// a credential into a name the transport also uses - User-Agent, Host, Accept,
// Content-Type, Content-Length - has that value carried across the boundary
// with the name. Such a credential is also sent to the origin and to any proxy
// in path, so it should not be placed there whether or not a redirect follows.
type AuthMethod interface {
transport.AuthMethod
SetAuth(r *http.Request)
@@ -598,6 +955,6 @@ func (e *Err) StatusCode() int {
func (e *Err) Error() string {
return fmt.Sprintf("unexpected requesting %q status code: %d",
e.Response.Request.URL, e.Response.StatusCode,
redactedURL(e.Response.Request.URL), e.Response.StatusCode,
)
}
@@ -88,7 +88,7 @@ func (s *rpSession) doRequest(
body = content
}
req, err := http.NewRequest(method, url, body)
req, err := newRequest(method, url, body)
if err != nil {
return nil, plumbing.NewPermanentError(err)
}
+1 -1
View File
@@ -86,7 +86,7 @@ func (s *upSession) doRequest(
body = content
}
req, err := http.NewRequest(method, url, body)
req, err := newRequest(method, url, body)
if err != nil {
return nil, plumbing.NewPermanentError(err)
}
+1
View File
@@ -433,6 +433,7 @@ func dotGitCommonDirectory(fs billy.Filesystem) (commonDir billy.Filesystem, err
if err != nil {
return nil, err
}
defer ioutil.CheckClose(f, &err)
b, err := io.ReadAll(f)
if err != nil {
+1
View File
@@ -1,2 +1,3 @@
* -text
*.bin -text -diff
*.md text eol=lf
+700 -700
View File
File diff suppressed because it is too large Load Diff
+78 -78
View File
@@ -1,79 +1,79 @@
# Finite State Entropy
This package provides Finite State Entropy encoding and decoding.
Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS))
encoding provides a fast near-optimal symbol encoding/decoding
for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd).
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse)
## News
* Feb 2018: First implementation released. Consider this beta software for now.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `(error)` | An internal error occurred. |
As can be seen above there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples).
# Performance
A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors.
All compression functions are currently only running on the calling goroutine so only one core will be used per block.
The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input
is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be
beneficial to transpose all your input values down by 64.
With moderate block sizes around 64k speed are typically 200MB/s per core for compression and
around 300MB/s decompression speed.
The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s.
# Plans
At one point, more internals will be exposed to facilitate more "expert" usage of the components.
A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261).
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
# Finite State Entropy
This package provides Finite State Entropy encoding and decoding.
Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS))
encoding provides a fast near-optimal symbol encoding/decoding
for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd).
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse)
## News
* Feb 2018: First implementation released. Consider this beta software for now.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `(error)` | An internal error occurred. |
As can be seen above there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples).
# Performance
A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors.
All compression functions are currently only running on the calling goroutine so only one core will be used per block.
The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input
is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be
beneficial to transpose all your input values down by 64.
With moderate block sizes around 64k speed are typically 200MB/s per core for compression and
around 300MB/s decompression speed.
The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s.
# Plans
At one point, more internals will be exposed to facilitate more "expert" usage of the components.
A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261).
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
changes will likely not be accepted. If in doubt open an issue before writing the PR.
+89 -89
View File
@@ -1,89 +1,89 @@
# Huff0 entropy compression
This package provides Huff0 encoding and decoding as used in zstd.
[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders),
a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU
(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds.
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
## News
This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package.
This ensures that most functionality is well tested.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and
[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) |
| `(error)` | An internal error occurred. |
As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding.
## Tables and re-use
Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results.
The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy)
that controls this behaviour. See the documentation for details. This can be altered between each block.
Do however note that this information is *not* stored in the output block and it is up to the users of the package to
record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called,
based on the boolean reported back from the CompressXX call.
If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the
[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object.
## Decompressing
The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable).
This will initialize the decoding tables.
You can supply the complete block to `ReadTable` and it will return the data part of the block
which can be given to the decompressor.
Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
changes will likely not be accepted. If in doubt open an issue before writing the PR.
# Huff0 entropy compression
This package provides Huff0 encoding and decoding as used in zstd.
[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders),
a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU
(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds.
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
## News
This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package.
This ensures that most functionality is well tested.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and
[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) |
| `(error)` | An internal error occurred. |
As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding.
## Tables and re-use
Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results.
The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy)
that controls this behaviour. See the documentation for details. This can be altered between each block.
Do however note that this information is *not* stored in the output block and it is up to the users of the package to
record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called,
based on the boolean reported back from the CompressXX call.
If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the
[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object.
## Decompressing
The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable).
This will initialize the decoding tables.
You can supply the complete block to `ReadTable` and it will return the data part of the block
which can be given to the decompressor.
Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
changes will likely not be accepted. If in doubt open an issue before writing the PR.
-24
View File
@@ -1,24 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
-57
View File
@@ -1,57 +0,0 @@
version: 2
builds:
-
id: "cpuid"
binary: cpuid
main: ./cmd/cpuid/main.go
env:
- CGO_ENABLED=0
flags:
- -ldflags=-s -w
goos:
- aix
- linux
- freebsd
- netbsd
- windows
- darwin
goarch:
- 386
- amd64
- arm64
goarm:
- 7
archives:
-
id: cpuid
name_template: "cpuid-{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
format_overrides:
- goos: windows
format: zip
files:
- LICENSE
checksum:
name_template: 'checksums.txt'
changelog:
sort: asc
filters:
exclude:
- '^doc:'
- '^docs:'
- '^test:'
- '^tests:'
- '^Update\sREADME.md'
nfpms:
-
file_name_template: "cpuid_package_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
vendor: Klaus Post
homepage: https://github.com/klauspost/cpuid
maintainer: Klaus Post <klauspost@gmail.com>
description: CPUID Tool
license: BSD 3-Clause
formats:
- deb
- rpm
-35
View File
@@ -1,35 +0,0 @@
Developer Certificate of Origin
Version 1.1
Copyright (C) 2015- Klaus Post & Contributors.
Email: klauspost@gmail.com
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
-22
View File
@@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Klaus Post
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-512
View File
@@ -1,512 +0,0 @@
# cpuid
Package cpuid provides information about the CPU running the current program.
CPU features are detected on startup, and kept for fast access through the life of the application.
Currently x86 / x64 (AMD64/i386) and ARM (ARM64) is supported, and no external C (cgo) code is used, which should make the library very easy to use.
You can access the CPU information by accessing the shared CPU variable of the cpuid library.
Package home: https://github.com/klauspost/cpuid
[![PkgGoDev](https://pkg.go.dev/badge/github.com/klauspost/cpuid)](https://pkg.go.dev/github.com/klauspost/cpuid/v2)
[![Go](https://github.com/klauspost/cpuid/actions/workflows/go.yml/badge.svg)](https://github.com/klauspost/cpuid/actions/workflows/go.yml)
## installing
`go get -u github.com/klauspost/cpuid/v2` using modules.
Drop `v2` for others.
Installing binary:
`go install github.com/klauspost/cpuid/v2/cmd/cpuid@latest`
Or download binaries from release page: https://github.com/klauspost/cpuid/releases
### Homebrew
For macOS/Linux users, you can install via [brew](https://brew.sh/)
```sh
$ brew install cpuid
```
## example
```Go
package main
import (
"fmt"
"strings"
. "github.com/klauspost/cpuid/v2"
)
func main() {
// Print basic CPU information:
fmt.Println("Name:", CPU.BrandName)
fmt.Println("PhysicalCores:", CPU.PhysicalCores)
fmt.Println("ThreadsPerCore:", CPU.ThreadsPerCore)
fmt.Println("LogicalCores:", CPU.LogicalCores)
fmt.Println("Family", CPU.Family, "Model:", CPU.Model, "Vendor ID:", CPU.VendorID)
fmt.Println("Features:", strings.Join(CPU.FeatureSet(), ","))
fmt.Println("Cacheline bytes:", CPU.CacheLine)
fmt.Println("L1 Data Cache:", CPU.Cache.L1D, "bytes")
fmt.Println("L1 Instruction Cache:", CPU.Cache.L1I, "bytes")
fmt.Println("L2 Cache:", CPU.Cache.L2, "bytes")
fmt.Println("L3 Cache:", CPU.Cache.L3, "bytes")
fmt.Println("Frequency", CPU.Hz, "hz")
// Test if we have these specific features:
if CPU.Supports(SSE, SSE2) {
fmt.Println("We have Streaming SIMD 2 Extensions")
}
}
```
Sample output:
```
>go run main.go
Name: AMD Ryzen 9 3950X 16-Core Processor
PhysicalCores: 16
ThreadsPerCore: 2
LogicalCores: 32
Family 23 Model: 113 Vendor ID: AMD
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CMOV,CX16,F16C,FMA3,HTT,HYPERVISOR,LZCNT,MMX,MMXEXT,NX,POPCNT,RDRAND,RDSEED,RDTSCP,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3
Cacheline bytes: 64
L1 Data Cache: 32768 bytes
L1 Instruction Cache: 32768 bytes
L2 Cache: 524288 bytes
L3 Cache: 16777216 bytes
Frequency 0 hz
We have Streaming SIMD 2 Extensions
```
# usage
The `cpuid.CPU` provides access to CPU features. Use `cpuid.CPU.Supports()` to check for CPU features.
A faster `cpuid.CPU.Has()` is provided which will usually be inlined by the gc compiler.
To test a larger number of features, they can be combined using `f := CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2)`, etc.
This can be using with `cpuid.CPU.HasAll(f)` to quickly test if all features are supported.
Note that for some cpu/os combinations some features will not be detected.
`amd64` has rather good support and should work reliably on all platforms.
Note that hypervisors may not pass through all CPU features through to the guest OS,
so even if your host supports a feature it may not be visible on guests.
## arm64 feature detection
Not all operating systems provide ARM features directly
and there is no safe way to do so for the rest.
Currently `arm64/linux` and `arm64/freebsd` should be quite reliable.
`arm64/darwin` adds features expected from the M1 processor, but a lot remains undetected.
A `DetectARM()` can be used if you are able to control your deployment,
it will detect CPU features, but may crash if the OS doesn't intercept the calls.
A `-cpu.arm` flag for detecting unsafe ARM features can be added. See below.
Note that currently only features are detected on ARM,
no additional information is currently available.
## flags
It is possible to add flags that affects cpu detection.
For this the `Flags()` command is provided.
This must be called *before* `flag.Parse()` AND after the flags have been parsed `Detect()` must be called.
This means that any detection used in `init()` functions will not contain these flags.
Example:
```Go
package main
import (
"flag"
"fmt"
"strings"
"github.com/klauspost/cpuid/v2"
)
func main() {
cpuid.Flags()
flag.Parse()
cpuid.Detect()
// Test if we have these specific features:
if cpuid.CPU.Supports(cpuid.SSE, cpuid.SSE2) {
fmt.Println("We have Streaming SIMD 2 Extensions")
}
}
```
## commandline
Download as binary from: https://github.com/klauspost/cpuid/releases
Install from source:
`go install github.com/klauspost/cpuid/v2/cmd/cpuid@latest`
### Example
```
λ cpuid
Name: AMD Ryzen 9 3950X 16-Core Processor
Vendor String: AuthenticAMD
Vendor ID: AMD
PhysicalCores: 16
Threads Per Core: 2
Logical Cores: 32
CPU Family 23 Model: 113
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CLZERO,CMOV,CMPXCHG8,CPBOOST,CX16,F16C,FMA3,FXSR,FXSROPT,HTT,HYPERVISOR,LAHF,LZCNT,MCAOVERFLOW,MMX,MMXEXT,MOVBE,NX,OSXSAVE,POPCNT,RDRAND,RDSEED,RDTSCP,SCE,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3,SUCCOR,X87,XSAVE
Microarchitecture level: 3
Cacheline bytes: 64
L1 Instruction Cache: 32768 bytes
L1 Data Cache: 32768 bytes
L2 Cache: 524288 bytes
L3 Cache: 16777216 bytes
```
### JSON Output:
```
λ cpuid --json
{
"BrandName": "AMD Ryzen 9 3950X 16-Core Processor",
"VendorID": 2,
"VendorString": "AuthenticAMD",
"PhysicalCores": 16,
"ThreadsPerCore": 2,
"LogicalCores": 32,
"Family": 23,
"Model": 113,
"CacheLine": 64,
"Hz": 0,
"BoostFreq": 0,
"Cache": {
"L1I": 32768,
"L1D": 32768,
"L2": 524288,
"L3": 16777216
},
"SGX": {
"Available": false,
"LaunchControl": false,
"SGX1Supported": false,
"SGX2Supported": false,
"MaxEnclaveSizeNot64": 0,
"MaxEnclaveSize64": 0,
"EPCSections": null
},
"Features": [
"ADX",
"AESNI",
"AVX",
"AVX2",
"BMI1",
"BMI2",
"CLMUL",
"CLZERO",
"CMOV",
"CMPXCHG8",
"CPBOOST",
"CX16",
"F16C",
"FMA3",
"FXSR",
"FXSROPT",
"HTT",
"HYPERVISOR",
"LAHF",
"LZCNT",
"MCAOVERFLOW",
"MMX",
"MMXEXT",
"MOVBE",
"NX",
"OSXSAVE",
"POPCNT",
"RDRAND",
"RDSEED",
"RDTSCP",
"SCE",
"SHA",
"SSE",
"SSE2",
"SSE3",
"SSE4",
"SSE42",
"SSE4A",
"SSSE3",
"SUCCOR",
"X87",
"XSAVE"
],
"X64Level": 3
}
```
### Check CPU microarch level
```
λ cpuid --check-level=3
2022/03/18 17:04:40 AMD Ryzen 9 3950X 16-Core Processor
2022/03/18 17:04:40 Microarchitecture level 3 is supported. Max level is 3.
Exit Code 0
λ cpuid --check-level=4
2022/03/18 17:06:18 AMD Ryzen 9 3950X 16-Core Processor
2022/03/18 17:06:18 Microarchitecture level 4 not supported. Max level is 3.
Exit Code 1
```
## Available flags
### x86 & amd64
| Feature Flag | Description |
|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ADX | Intel ADX (Multi-Precision Add-Carry Instruction Extensions) |
| AESNI | Advanced Encryption Standard New Instructions |
| AMD3DNOW | AMD 3DNOW |
| AMD3DNOWEXT | AMD 3DNowExt |
| AMXBF16 | Tile computational operations on BFLOAT16 numbers |
| AMXINT8 | Tile computational operations on 8-bit integers |
| AMXFP16 | Tile computational operations on FP16 numbers |
| AMXFP8 | Tile computational operations on FP8 numbers |
| AMXCOMPLEX | Tile computational operations on complex numbers |
| AMXTILE | Tile architecture |
| AMXTF32 | Matrix Multiplication of TF32 Tiles into Packed Single Precision Tile |
| AMXTRANSPOSE | Tile multiply where the first operand is transposed |
| APX_F | Intel APX |
| AVX | AVX functions |
| AVX10 | If set the Intel AVX10 Converged Vector ISA is supported |
| AVX10_128 | If set indicates that AVX10 128-bit vector support is present |
| AVX10_256 | If set indicates that AVX10 256-bit vector support is present |
| AVX10_512 | If set indicates that AVX10 512-bit vector support is present |
| AVX2 | AVX2 functions |
| AVX512BF16 | AVX-512 BFLOAT16 Instructions |
| AVX512BITALG | AVX-512 Bit Algorithms |
| AVX512BW | AVX-512 Byte and Word Instructions |
| AVX512CD | AVX-512 Conflict Detection Instructions |
| AVX512DQ | AVX-512 Doubleword and Quadword Instructions |
| AVX512ER | AVX-512 Exponential and Reciprocal Instructions |
| AVX512F | AVX-512 Foundation |
| AVX512FP16 | AVX-512 FP16 Instructions |
| AVX512IFMA | AVX-512 Integer Fused Multiply-Add Instructions |
| AVX512PF | AVX-512 Prefetch Instructions |
| AVX512VBMI | AVX-512 Vector Bit Manipulation Instructions |
| AVX512VBMI2 | AVX-512 Vector Bit Manipulation Instructions, Version 2 |
| AVX512VL | AVX-512 Vector Length Extensions |
| AVX512VNNI | AVX-512 Vector Neural Network Instructions |
| AVX512VP2INTERSECT | AVX-512 Intersect for D/Q |
| AVX512VPOPCNTDQ | AVX-512 Vector Population Count Doubleword and Quadword |
| AVXIFMA | AVX-IFMA instructions |
| AVXNECONVERT | AVX-NE-CONVERT instructions |
| AVXSLOW | Indicates the CPU performs 2 128 bit operations instead of one |
| AVXVNNI | AVX (VEX encoded) VNNI neural network instructions |
| AVXVNNIINT8 | AVX-VNNI-INT8 instructions |
| AVXVNNIINT16 | AVX-VNNI-INT16 instructions |
| BHI_CTRL | Branch History Injection and Intra-mode Branch Target Injection / CVE-2022-0001, CVE-2022-0002 / INTEL-SA-00598 |
| BMI1 | Bit Manipulation Instruction Set 1 |
| BMI2 | Bit Manipulation Instruction Set 2 |
| CETIBT | Intel CET Indirect Branch Tracking |
| CETSS | Intel CET Shadow Stack |
| CLDEMOTE | Cache Line Demote |
| CLMUL | Carry-less Multiplication |
| CLZERO | CLZERO instruction supported |
| CMOV | i686 CMOV |
| CMPCCXADD | CMPCCXADD instructions |
| CMPSB_SCADBS_SHORT | Fast short CMPSB and SCASB |
| CMPXCHG8 | CMPXCHG8 instruction |
| CPBOOST | Core Performance Boost |
| CPPC | AMD: Collaborative Processor Performance Control |
| CX16 | CMPXCHG16B Instruction |
| EFER_LMSLE_UNS | AMD: =Core::X86::Msr::EFER[LMSLE] is not supported, and MBZ |
| ENQCMD | Enqueue Command |
| ERMS | Enhanced REP MOVSB/STOSB |
| F16C | Half-precision floating-point conversion |
| FLUSH_L1D | Flush L1D cache |
| FMA3 | Intel FMA 3. Does not imply AVX. |
| FMA4 | Bulldozer FMA4 functions |
| FP128 | AMD: When set, the internal FP/SIMD execution datapath is 128-bits wide |
| FP256 | AMD: When set, the internal FP/SIMD execution datapath is 256-bits wide |
| FSRM | Fast Short Rep Mov |
| FXSR | FXSAVE, FXRESTOR instructions, CR4 bit 9 |
| FXSROPT | FXSAVE/FXRSTOR optimizations |
| GFNI | Galois Field New Instructions. May require other features (AVX, AVX512VL,AVX512F) based on usage. |
| HLE | Hardware Lock Elision |
| HRESET | If set CPU supports history reset and the IA32_HRESET_ENABLE MSR |
| HTT | Hyperthreading (enabled) |
| HWA | Hardware assert supported. Indicates support for MSRC001_10 |
| HYBRID_CPU | This part has CPUs of more than one type. |
| HYPERVISOR | This bit has been reserved by Intel & AMD for use by hypervisors |
| IA32_ARCH_CAP | IA32_ARCH_CAPABILITIES MSR (Intel) |
| IA32_CORE_CAP | IA32_CORE_CAPABILITIES MSR |
| IBPB | Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB) |
| IBRS | AMD: Indirect Branch Restricted Speculation |
| IBRS_PREFERRED | AMD: IBRS is preferred over software solution |
| IBRS_PROVIDES_SMP | AMD: IBRS provides Same Mode Protection |
| IBS | Instruction Based Sampling (AMD) |
| IBSBRNTRGT | Instruction Based Sampling Feature (AMD) |
| IBSFETCHSAM | Instruction Based Sampling Feature (AMD) |
| IBSFFV | Instruction Based Sampling Feature (AMD) |
| IBSOPCNT | Instruction Based Sampling Feature (AMD) |
| IBSOPCNTEXT | Instruction Based Sampling Feature (AMD) |
| IBSOPSAM | Instruction Based Sampling Feature (AMD) |
| IBSRDWROPCNT | Instruction Based Sampling Feature (AMD) |
| IBSRIPINVALIDCHK | Instruction Based Sampling Feature (AMD) |
| IBS_FETCH_CTLX | AMD: IBS fetch control extended MSR supported |
| IBS_OPDATA4 | AMD: IBS op data 4 MSR supported |
| IBS_OPFUSE | AMD: Indicates support for IbsOpFuse |
| IBS_PREVENTHOST | Disallowing IBS use by the host supported |
| IBS_ZEN4 | Fetch and Op IBS support IBS extensions added with Zen4 |
| IDPRED_CTRL | IPRED_DIS |
| INT_WBINVD | WBINVD/WBNOINVD are interruptible. |
| INVLPGB | NVLPGB and TLBSYNC instruction supported |
| KEYLOCKER | Key locker |
| KEYLOCKERW | Key locker wide |
| LAHF | LAHF/SAHF in long mode |
| LAM | If set, CPU supports Linear Address Masking |
| LBRVIRT | LBR virtualization |
| LZCNT | LZCNT instruction |
| MCAOVERFLOW | MCA overflow recovery support. |
| MCDT_NO | Processor do not exhibit MXCSR Configuration Dependent Timing behavior and do not need to mitigate it. |
| MCOMMIT | MCOMMIT instruction supported |
| MD_CLEAR | VERW clears CPU buffers |
| MMX | standard MMX |
| MMXEXT | SSE integer functions or AMD MMX ext |
| MOVBE | MOVBE instruction (big-endian) |
| MOVDIR64B | Move 64 Bytes as Direct Store |
| MOVDIRI | Move Doubleword as Direct Store |
| MOVSB_ZL | Fast Zero-Length MOVSB |
| MPX | Intel MPX (Memory Protection Extensions) |
| MOVU | MOVU SSE instructions are more efficient and should be preferred to SSE MOVL/MOVH. MOVUPS is more efficient than MOVLPS/MOVHPS. MOVUPD is more efficient than MOVLPD/MOVHPD |
| MSRIRC | Instruction Retired Counter MSR available |
| MSRLIST | Read/Write List of Model Specific Registers |
| MSR_PAGEFLUSH | Page Flush MSR available |
| NRIPS | Indicates support for NRIP save on VMEXIT |
| NX | NX (No-Execute) bit |
| OSXSAVE | XSAVE enabled by OS |
| PCONFIG | PCONFIG for Intel Multi-Key Total Memory Encryption |
| POPCNT | POPCNT instruction |
| PPIN | AMD: Protected Processor Inventory Number support. Indicates that Protected Processor Inventory Number (PPIN) capability can be enabled |
| PREFETCHI | PREFETCHIT0/1 instructions |
| PSFD | Predictive Store Forward Disable |
| RDPRU | RDPRU instruction supported |
| RDRAND | RDRAND instruction is available |
| RDSEED | RDSEED instruction is available |
| RDTSCP | RDTSCP Instruction |
| RRSBA_CTRL | Restricted RSB Alternate |
| RTM | Restricted Transactional Memory |
| RTM_ALWAYS_ABORT | Indicates that the loaded microcode is forcing RTM abort. |
| SERIALIZE | Serialize Instruction Execution |
| SEV | AMD Secure Encrypted Virtualization supported |
| SEV_64BIT | AMD SEV guest execution only allowed from a 64-bit host |
| SEV_ALTERNATIVE | AMD SEV Alternate Injection supported |
| SEV_DEBUGSWAP | Full debug state swap supported for SEV-ES guests |
| SEV_ES | AMD SEV Encrypted State supported |
| SEV_RESTRICTED | AMD SEV Restricted Injection supported |
| SEV_SNP | AMD SEV Secure Nested Paging supported |
| SGX | Software Guard Extensions |
| SGXLC | Software Guard Extensions Launch Control |
| SGXPQC | Software Guard Extensions 256-bit Encryption |
| SHA | Intel SHA Extensions |
| SME | AMD Secure Memory Encryption supported |
| SME_COHERENT | AMD Hardware cache coherency across encryption domains enforced |
| SM3_X86 | SM3 instructions |
| SM4_X86 | SM4 instructions |
| SPEC_CTRL_SSBD | Speculative Store Bypass Disable |
| SRBDS_CTRL | SRBDS mitigation MSR available |
| SSE | SSE functions |
| SSE2 | P4 SSE functions |
| SSE3 | Prescott SSE3 functions |
| SSE4 | Penryn SSE4.1 functions |
| SSE42 | Nehalem SSE4.2 functions |
| SSE4A | AMD Barcelona microarchitecture SSE4a instructions |
| SSSE3 | Conroe SSSE3 functions |
| STIBP | Single Thread Indirect Branch Predictors |
| STIBP_ALWAYSON | AMD: Single Thread Indirect Branch Prediction Mode has Enhanced Performance and may be left Always On |
| STOSB_SHORT | Fast short STOSB |
| SUCCOR | Software uncorrectable error containment and recovery capability. |
| SVM | AMD Secure Virtual Machine |
| SVMDA | Indicates support for the SVM decode assists. |
| SVMFBASID | SVM, Indicates that TLB flush events, including CR3 writes and CR4.PGE toggles, flush only the current ASID's TLB entries. Also indicates support for the extended VMCBTLB_Control |
| SVML | AMD SVM lock. Indicates support for SVM-Lock. |
| SVMNP | AMD SVM nested paging |
| SVMPF | SVM pause intercept filter. Indicates support for the pause intercept filter |
| SVMPFT | SVM PAUSE filter threshold. Indicates support for the PAUSE filter cycle count threshold |
| SYSCALL | System-Call Extension (SCE): SYSCALL and SYSRET instructions. |
| SYSEE | SYSENTER and SYSEXIT instructions |
| TBM | AMD Trailing Bit Manipulation |
| TDX_GUEST | Intel Trust Domain Extensions Guest |
| TLB_FLUSH_NESTED | AMD: Flushing includes all the nested translations for guest translations |
| TME | Intel Total Memory Encryption. The following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE. |
| TOPEXT | TopologyExtensions: topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX. |
| TSA_L1_NO | AMD only: Not vulnerable to TSA-L1 |
| TSA_SQ_NO | AMD only: Not vulnerable to TSA-SQ |
| TSA_VERW_CLEAR | AMD: If set, the memory form of the VERW instruction may be used to help mitigate TSA |
| TSCRATEMSR | MSR based TSC rate control. Indicates support for MSR TSC ratio MSRC000_0104 |
| TSXLDTRK | Intel TSX Suspend Load Address Tracking |
| VAES | Vector AES. AVX(512) versions requires additional checks. |
| VMCBCLEAN | VMCB clean bits. Indicates support for VMCB clean bits. |
| VMPL | AMD VM Permission Levels supported |
| VMSA_REGPROT | AMD VMSA Register Protection supported |
| VMX | Virtual Machine Extensions |
| VPCLMULQDQ | Carry-Less Multiplication Quadword. Requires AVX for 3 register versions. |
| VTE | AMD Virtual Transparent Encryption supported |
| WAITPKG | TPAUSE, UMONITOR, UMWAIT |
| WBNOINVD | Write Back and Do Not Invalidate Cache |
| WRMSRNS | Non-Serializing Write to Model Specific Register |
| X87 | FPU |
| XGETBV1 | Supports XGETBV with ECX = 1 |
| XOP | Bulldozer XOP functions |
| XSAVE | XSAVE, XRESTOR, XSETBV, XGETBV |
| XSAVEC | Supports XSAVEC and the compacted form of XRSTOR. |
| XSAVEOPT | XSAVEOPT available |
| XSAVES | Supports XSAVES/XRSTORS and IA32_XSS |
# ARM features:
| Feature Flag | Description |
|--------------|------------------------------------------------------------------|
| AESARM | AES instructions |
| ARMCPUID | Some CPU ID registers readable at user-level |
| ASIMD | Advanced SIMD |
| ASIMDDP | SIMD Dot Product |
| ASIMDHP | Advanced SIMD half-precision floating point |
| ASIMDRDM | Rounding Double Multiply Accumulate/Subtract (SQRDMLAH/SQRDMLSH) |
| ATOMICS | Large System Extensions (LSE) |
| CRC32 | CRC32/CRC32C instructions |
| DCPOP | Data cache clean to Point of Persistence (DC CVAP) |
| EVTSTRM | Generic timer |
| FCMA | Floatin point complex number addition and multiplication |
| FHM | FMLAL and FMLSL instructions |
| FP | Single-precision and double-precision floating point |
| FPHP | Half-precision floating point |
| GPA | Generic Pointer Authentication |
| JSCVT | Javascript-style double->int convert (FJCVTZS) |
| LRCPC | Weaker release consistency (LDAPR, etc) |
| PMULL | Polynomial Multiply instructions (PMULL/PMULL2) |
| RNDR | Random Number instructions |
| TLB | Outer Shareable and TLB range maintenance instructions |
| TS | Flag manipulation instructions |
| SHA1 | SHA-1 instructions (SHA1C, etc) |
| SHA2 | SHA-2 instructions (SHA256H, etc) |
| SHA3 | SHA-3 instructions (EOR3, RAXI, XAR, BCAX) |
| SHA512 | SHA512 instructions |
| SM3 | SM3 instructions |
| SM4 | SM4 instructions |
| SVE | Scalable Vector Extension |
# license
This code is published under an MIT license. See LICENSE file for more information.
-1679
View File
File diff suppressed because it is too large Load Diff
-47
View File
@@ -1,47 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build 386,!gccgo,!noasm,!appengine
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuid(SB), 7, $0
XORL CX, CX
MOVL op+0(FP), AX
CPUID
MOVL AX, eax+4(FP)
MOVL BX, ebx+8(FP)
MOVL CX, ecx+12(FP)
MOVL DX, edx+16(FP)
RET
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuidex(SB), 7, $0
MOVL op+0(FP), AX
MOVL op2+4(FP), CX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func xgetbv(index uint32) (eax, edx uint32)
TEXT ·asmXgetbv(SB), 7, $0
MOVL index+0(FP), CX
BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
MOVL AX, eax+4(FP)
MOVL DX, edx+8(FP)
RET
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
TEXT ·asmRdtscpAsm(SB), 7, $0
BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
MOVL AX, eax+0(FP)
MOVL BX, ebx+4(FP)
MOVL CX, ecx+8(FP)
MOVL DX, edx+12(FP)
RET
// func asmDarwinHasAVX512() bool
TEXT ·asmDarwinHasAVX512(SB), 7, $0
MOVL $0, eax+0(FP)
RET
-72
View File
@@ -1,72 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build amd64,!gccgo,!noasm,!appengine
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuid(SB), 7, $0
XORQ CX, CX
MOVL op+0(FP), AX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuidex(SB), 7, $0
MOVL op+0(FP), AX
MOVL op2+4(FP), CX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func asmXgetbv(index uint32) (eax, edx uint32)
TEXT ·asmXgetbv(SB), 7, $0
MOVL index+0(FP), CX
BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
MOVL AX, eax+8(FP)
MOVL DX, edx+12(FP)
RET
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
TEXT ·asmRdtscpAsm(SB), 7, $0
BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
MOVL AX, eax+0(FP)
MOVL BX, ebx+4(FP)
MOVL CX, ecx+8(FP)
MOVL DX, edx+12(FP)
RET
// From https://go-review.googlesource.com/c/sys/+/285572/
// func asmDarwinHasAVX512() bool
TEXT ·asmDarwinHasAVX512(SB), 7, $0-1
MOVB $0, ret+0(FP) // default to false
#ifdef GOOS_darwin // return if not darwin
#ifdef GOARCH_amd64 // return if not amd64
// These values from:
// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h
#define commpage64_base_address 0x00007fffffe00000
#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010)
#define commpage64_version (commpage64_base_address+0x01E)
#define hasAVX512F 0x0000004000000000
MOVQ $commpage64_version, BX
MOVW (BX), AX
CMPW AX, $13 // versions < 13 do not support AVX512
JL no_avx512
MOVQ $commpage64_cpu_capabilities64, BX
MOVQ (BX), AX
MOVQ $hasAVX512F, CX
ANDQ CX, AX
JZ no_avx512
MOVB $1, ret+0(FP)
no_avx512:
#endif
#endif
RET
-36
View File
@@ -1,36 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build arm64,!gccgo,!noasm,!appengine
// See https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt
// func getMidr
TEXT ·getMidr(SB), 7, $0
WORD $0xd5380000 // mrs x0, midr_el1 /* Main ID Register */
MOVD R0, midr+0(FP)
RET
// func getProcFeatures
TEXT ·getProcFeatures(SB), 7, $0
WORD $0xd5380400 // mrs x0, id_aa64pfr0_el1 /* Processor Feature Register 0 */
MOVD R0, procFeatures+0(FP)
RET
// func getInstAttributes
TEXT ·getInstAttributes(SB), 7, $0
WORD $0xd5380600 // mrs x0, id_aa64isar0_el1 /* Instruction Set Attribute Register 0 */
WORD $0xd5380621 // mrs x1, id_aa64isar1_el1 /* Instruction Set Attribute Register 1 */
MOVD R0, instAttrReg0+0(FP)
MOVD R1, instAttrReg1+8(FP)
RET
TEXT ·getVectorLength(SB), 7, $0
WORD $0xd2800002 // mov x2, #0
WORD $0x04225022 // addvl x2, x2, #1
WORD $0xd37df042 // lsl x2, x2, #3
WORD $0xd2800003 // mov x3, #0
WORD $0x04635023 // addpl x3, x3, #1
WORD $0xd37df063 // lsl x3, x3, #3
MOVD R2, vl+0(FP)
MOVD R3, pl+8(FP)
RET
-250
View File
@@ -1,250 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//go:build arm64 && !gccgo && !noasm && !appengine
// +build arm64,!gccgo,!noasm,!appengine
package cpuid
import "runtime"
func getMidr() (midr uint64)
func getProcFeatures() (procFeatures uint64)
func getInstAttributes() (instAttrReg0, instAttrReg1 uint64)
func getVectorLength() (vl, pl uint64)
func initCPU() {
cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
}
func addInfo(c *CPUInfo, safe bool) {
// Seems to be safe to assume on ARM64
c.CacheLine = 64
detectOS(c)
// ARM64 disabled since it may crash if interrupt is not intercepted by OS.
if safe && !c.Has(ARMCPUID) && runtime.GOOS != "freebsd" {
return
}
midr := getMidr()
// MIDR_EL1 - Main ID Register
// https://developer.arm.com/docs/ddi0595/h/aarch64-system-registers/midr_el1
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | Implementer | [31-24] | y |
// |--------------------------------------------------|
// | Variant | [23-20] | y |
// |--------------------------------------------------|
// | Architecture | [19-16] | y |
// |--------------------------------------------------|
// | PartNum | [15-4] | y |
// |--------------------------------------------------|
// | Revision | [3-0] | y |
// x--------------------------------------------------x
switch (midr >> 24) & 0xff {
case 0xC0:
c.VendorString = "Ampere Computing"
c.VendorID = Ampere
case 0x41:
c.VendorString = "Arm Limited"
c.VendorID = ARM
case 0x42:
c.VendorString = "Broadcom Corporation"
c.VendorID = Broadcom
case 0x43:
c.VendorString = "Cavium Inc"
c.VendorID = Cavium
case 0x44:
c.VendorString = "Digital Equipment Corporation"
c.VendorID = DEC
case 0x46:
c.VendorString = "Fujitsu Ltd"
c.VendorID = Fujitsu
case 0x49:
c.VendorString = "Infineon Technologies AG"
c.VendorID = Infineon
case 0x4D:
c.VendorString = "Motorola or Freescale Semiconductor Inc"
c.VendorID = Motorola
case 0x4E:
c.VendorString = "NVIDIA Corporation"
c.VendorID = NVIDIA
case 0x50:
c.VendorString = "Applied Micro Circuits Corporation"
c.VendorID = AMCC
case 0x51:
c.VendorString = "Qualcomm Inc"
c.VendorID = Qualcomm
case 0x56:
c.VendorString = "Marvell International Ltd"
c.VendorID = Marvell
case 0x69:
c.VendorString = "Intel Corporation"
c.VendorID = Intel
}
// Lower 4 bits: Architecture
// Architecture Meaning
// 0b0001 Armv4.
// 0b0010 Armv4T.
// 0b0011 Armv5 (obsolete).
// 0b0100 Armv5T.
// 0b0101 Armv5TE.
// 0b0110 Armv5TEJ.
// 0b0111 Armv6.
// 0b1111 Architectural features are individually identified in the ID_* registers, see 'ID registers'.
// Upper 4 bit: Variant
// An IMPLEMENTATION DEFINED variant number.
// Typically, this field is used to distinguish between different product variants, or major revisions of a product.
c.Family = int(midr>>16) & 0xff
// PartNum, bits [15:4]
// An IMPLEMENTATION DEFINED primary part number for the device.
// On processors implemented by Arm, if the top four bits of the primary
// part number are 0x0 or 0x7, the variant and architecture are encoded differently.
// Revision, bits [3:0]
// An IMPLEMENTATION DEFINED revision number for the device.
c.Model = int(midr) & 0xffff
procFeatures := getProcFeatures()
// ID_AA64PFR0_EL1 - Processor Feature Register 0
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | DIT | [51-48] | y |
// |--------------------------------------------------|
// | SVE | [35-32] | y |
// |--------------------------------------------------|
// | GIC | [27-24] | n |
// |--------------------------------------------------|
// | AdvSIMD | [23-20] | y |
// |--------------------------------------------------|
// | FP | [19-16] | y |
// |--------------------------------------------------|
// | EL3 | [15-12] | n |
// |--------------------------------------------------|
// | EL2 | [11-8] | n |
// |--------------------------------------------------|
// | EL1 | [7-4] | n |
// |--------------------------------------------------|
// | EL0 | [3-0] | n |
// x--------------------------------------------------x
var f flagSet
// if procFeatures&(0xf<<48) != 0 {
// fmt.Println("DIT")
// }
f.setIf(procFeatures&(0xf<<32) != 0, SVE)
if procFeatures&(0xf<<20) != 15<<20 {
f.set(ASIMD)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64pfr0_el1
// 0b0001 --> As for 0b0000, and also includes support for half-precision floating-point arithmetic.
f.setIf(procFeatures&(0xf<<20) == 1<<20, FPHP, ASIMDHP)
}
f.setIf(procFeatures&(0xf<<16) != 0, FP)
instAttrReg0, instAttrReg1 := getInstAttributes()
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
//
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | RNDR | [63-60] | y |
// |--------------------------------------------------|
// | TLB | [59-56] | y |
// |--------------------------------------------------|
// | TS | [55-52] | y |
// |--------------------------------------------------|
// | FHM | [51-48] | y |
// |--------------------------------------------------|
// | DP | [47-44] | y |
// |--------------------------------------------------|
// | SM4 | [43-40] | y |
// |--------------------------------------------------|
// | SM3 | [39-36] | y |
// |--------------------------------------------------|
// | SHA3 | [35-32] | y |
// |--------------------------------------------------|
// | RDM | [31-28] | y |
// |--------------------------------------------------|
// | ATOMICS | [23-20] | y |
// |--------------------------------------------------|
// | CRC32 | [19-16] | y |
// |--------------------------------------------------|
// | SHA2 | [15-12] | y |
// |--------------------------------------------------|
// | SHA1 | [11-8] | y |
// |--------------------------------------------------|
// | AES | [7-4] | y |
// x--------------------------------------------------x
f.setIf(instAttrReg0&(0xf<<60) != 0, RNDR)
f.setIf(instAttrReg0&(0xf<<56) != 0, TLB)
f.setIf(instAttrReg0&(0xf<<52) != 0, TS)
f.setIf(instAttrReg0&(0xf<<48) != 0, FHM)
f.setIf(instAttrReg0&(0xf<<44) != 0, ASIMDDP)
f.setIf(instAttrReg0&(0xf<<40) != 0, SM4)
f.setIf(instAttrReg0&(0xf<<36) != 0, SM3)
f.setIf(instAttrReg0&(0xf<<32) != 0, SHA3)
f.setIf(instAttrReg0&(0xf<<28) != 0, ASIMDRDM)
f.setIf(instAttrReg0&(0xf<<20) != 0, ATOMICS)
f.setIf(instAttrReg0&(0xf<<16) != 0, CRC32)
f.setIf(instAttrReg0&(0xf<<12) != 0, SHA2)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
// 0b0010 --> As 0b0001, plus SHA512H, SHA512H2, SHA512SU0, and SHA512SU1 instructions implemented.
f.setIf(instAttrReg0&(0xf<<12) == 2<<12, SHA512)
f.setIf(instAttrReg0&(0xf<<8) != 0, SHA1)
f.setIf(instAttrReg0&(0xf<<4) != 0, AESARM)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
// 0b0010 --> As for 0b0001, plus PMULL/PMULL2 instructions operating on 64-bit data quantities.
f.setIf(instAttrReg0&(0xf<<4) == 2<<4, PMULL)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar1_el1
//
// ID_AA64ISAR1_EL1 - Instruction set attribute register 1
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | GPI | [31-28] | y |
// |--------------------------------------------------|
// | GPA | [27-24] | y |
// |--------------------------------------------------|
// | LRCPC | [23-20] | y |
// |--------------------------------------------------|
// | FCMA | [19-16] | y |
// |--------------------------------------------------|
// | JSCVT | [15-12] | y |
// |--------------------------------------------------|
// | API | [11-8] | y |
// |--------------------------------------------------|
// | APA | [7-4] | y |
// |--------------------------------------------------|
// | DPB | [3-0] | y |
// x--------------------------------------------------x
// if instAttrReg1&(0xf<<28) != 0 {
// fmt.Println("GPI")
// }
f.setIf(instAttrReg1&(0xf<<28) != 24, GPA)
f.setIf(instAttrReg1&(0xf<<20) != 0, LRCPC)
f.setIf(instAttrReg1&(0xf<<16) != 0, FCMA)
f.setIf(instAttrReg1&(0xf<<12) != 0, JSCVT)
// if instAttrReg1&(0xf<<8) != 0 {
// fmt.Println("API")
// }
// if instAttrReg1&(0xf<<4) != 0 {
// fmt.Println("APA")
// }
f.setIf(instAttrReg1&(0xf<<0) != 0, DCPOP)
// Store
c.featureSet.or(f)
}
-17
View File
@@ -1,17 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//go:build (!amd64 && !386 && !arm64) || gccgo || noasm || appengine
// +build !amd64,!386,!arm64 gccgo noasm appengine
package cpuid
func initCPU() {
cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
}
func addInfo(info *CPUInfo, safe bool) {}
func getVectorLength() (vl, pl uint64) { return 0, 0 }
-45
View File
@@ -1,45 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//go:build (386 && !gccgo && !noasm && !appengine) || (amd64 && !gccgo && !noasm && !appengine)
// +build 386,!gccgo,!noasm,!appengine amd64,!gccgo,!noasm,!appengine
package cpuid
func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
func asmXgetbv(index uint32) (eax, edx uint32)
func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
func asmDarwinHasAVX512() bool
func initCPU() {
cpuid = asmCpuid
cpuidex = asmCpuidex
xgetbv = asmXgetbv
rdtscpAsm = asmRdtscpAsm
darwinHasAVX512 = asmDarwinHasAVX512
}
func addInfo(c *CPUInfo, safe bool) {
c.maxFunc = maxFunctionID()
c.maxExFunc = maxExtendedFunction()
c.BrandName = brandName()
c.CacheLine = cacheLine()
c.Family, c.Model, c.Stepping = familyModel()
c.featureSet = support()
c.SGX = hasSGX(c.featureSet.inSet(SGX), c.featureSet.inSet(SGXLC))
c.AMDMemEncryption = hasAMDMemEncryption(c.featureSet.inSet(SME) || c.featureSet.inSet(SEV))
c.ThreadsPerCore = threadsPerCore()
c.LogicalCores = logicalCores()
c.PhysicalCores = physicalCores()
c.VendorID, c.VendorString = vendorID()
c.HypervisorVendorID, c.HypervisorVendorString = hypervisorVendorID()
c.AVX10Level = c.supportAVX10()
c.cacheSize()
c.frequencies()
if c.maxFunc >= 0x0A {
eax, ebx, _, edx := cpuid(0x0A)
c.PMU = parseLeaf0AH(c, eax, ebx, edx)
}
}
func getVectorLength() (vl, pl uint64) { return 0, 0 }
-308
View File
@@ -1,308 +0,0 @@
// Code generated by "stringer -type=FeatureID,Vendor"; DO NOT EDIT.
package cpuid
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ADX-1]
_ = x[AESNI-2]
_ = x[AMD3DNOW-3]
_ = x[AMD3DNOWEXT-4]
_ = x[AMXBF16-5]
_ = x[AMXFP16-6]
_ = x[AMXINT8-7]
_ = x[AMXFP8-8]
_ = x[AMXTILE-9]
_ = x[AMXTF32-10]
_ = x[AMXCOMPLEX-11]
_ = x[AMXTRANSPOSE-12]
_ = x[APX_F-13]
_ = x[AVX-14]
_ = x[AVX10-15]
_ = x[AVX10_128-16]
_ = x[AVX10_256-17]
_ = x[AVX10_512-18]
_ = x[AVX2-19]
_ = x[AVX512BF16-20]
_ = x[AVX512BITALG-21]
_ = x[AVX512BW-22]
_ = x[AVX512CD-23]
_ = x[AVX512DQ-24]
_ = x[AVX512ER-25]
_ = x[AVX512F-26]
_ = x[AVX512FP16-27]
_ = x[AVX512IFMA-28]
_ = x[AVX512PF-29]
_ = x[AVX512VBMI-30]
_ = x[AVX512VBMI2-31]
_ = x[AVX512VL-32]
_ = x[AVX512VNNI-33]
_ = x[AVX512VP2INTERSECT-34]
_ = x[AVX512VPOPCNTDQ-35]
_ = x[AVXIFMA-36]
_ = x[AVXNECONVERT-37]
_ = x[AVXSLOW-38]
_ = x[AVXVNNI-39]
_ = x[AVXVNNIINT8-40]
_ = x[AVXVNNIINT16-41]
_ = x[BHI_CTRL-42]
_ = x[BMI1-43]
_ = x[BMI2-44]
_ = x[CETIBT-45]
_ = x[CETSS-46]
_ = x[CLDEMOTE-47]
_ = x[CLMUL-48]
_ = x[CLZERO-49]
_ = x[CMOV-50]
_ = x[CMPCCXADD-51]
_ = x[CMPSB_SCADBS_SHORT-52]
_ = x[CMPXCHG8-53]
_ = x[CPBOOST-54]
_ = x[CPPC-55]
_ = x[CX16-56]
_ = x[EFER_LMSLE_UNS-57]
_ = x[ENQCMD-58]
_ = x[ERMS-59]
_ = x[F16C-60]
_ = x[FLUSH_L1D-61]
_ = x[FMA3-62]
_ = x[FMA4-63]
_ = x[FP128-64]
_ = x[FP256-65]
_ = x[FSRM-66]
_ = x[FXSR-67]
_ = x[FXSROPT-68]
_ = x[GFNI-69]
_ = x[HLE-70]
_ = x[HRESET-71]
_ = x[HTT-72]
_ = x[HWA-73]
_ = x[HYBRID_CPU-74]
_ = x[HYPERVISOR-75]
_ = x[IA32_ARCH_CAP-76]
_ = x[IA32_CORE_CAP-77]
_ = x[IBPB-78]
_ = x[IBPB_BRTYPE-79]
_ = x[IBRS-80]
_ = x[IBRS_PREFERRED-81]
_ = x[IBRS_PROVIDES_SMP-82]
_ = x[IBS-83]
_ = x[IBSBRNTRGT-84]
_ = x[IBSFETCHSAM-85]
_ = x[IBSFFV-86]
_ = x[IBSOPCNT-87]
_ = x[IBSOPCNTEXT-88]
_ = x[IBSOPSAM-89]
_ = x[IBSRDWROPCNT-90]
_ = x[IBSRIPINVALIDCHK-91]
_ = x[IBS_FETCH_CTLX-92]
_ = x[IBS_OPDATA4-93]
_ = x[IBS_OPFUSE-94]
_ = x[IBS_PREVENTHOST-95]
_ = x[IBS_ZEN4-96]
_ = x[IDPRED_CTRL-97]
_ = x[INT_WBINVD-98]
_ = x[INVLPGB-99]
_ = x[KEYLOCKER-100]
_ = x[KEYLOCKERW-101]
_ = x[LAHF-102]
_ = x[LAM-103]
_ = x[LBRVIRT-104]
_ = x[LZCNT-105]
_ = x[MCAOVERFLOW-106]
_ = x[MCDT_NO-107]
_ = x[MCOMMIT-108]
_ = x[MD_CLEAR-109]
_ = x[MMX-110]
_ = x[MMXEXT-111]
_ = x[MOVBE-112]
_ = x[MOVDIR64B-113]
_ = x[MOVDIRI-114]
_ = x[MOVSB_ZL-115]
_ = x[MOVU-116]
_ = x[MPX-117]
_ = x[MSRIRC-118]
_ = x[MSRLIST-119]
_ = x[MSR_PAGEFLUSH-120]
_ = x[NRIPS-121]
_ = x[NX-122]
_ = x[OSXSAVE-123]
_ = x[PCONFIG-124]
_ = x[POPCNT-125]
_ = x[PPIN-126]
_ = x[PREFETCHI-127]
_ = x[PSFD-128]
_ = x[RDPRU-129]
_ = x[RDRAND-130]
_ = x[RDSEED-131]
_ = x[RDTSCP-132]
_ = x[RRSBA_CTRL-133]
_ = x[RTM-134]
_ = x[RTM_ALWAYS_ABORT-135]
_ = x[SBPB-136]
_ = x[SERIALIZE-137]
_ = x[SEV-138]
_ = x[SEV_64BIT-139]
_ = x[SEV_ALTERNATIVE-140]
_ = x[SEV_DEBUGSWAP-141]
_ = x[SEV_ES-142]
_ = x[SEV_RESTRICTED-143]
_ = x[SEV_SNP-144]
_ = x[SGX-145]
_ = x[SGXLC-146]
_ = x[SGXPQC-147]
_ = x[SHA-148]
_ = x[SME-149]
_ = x[SME_COHERENT-150]
_ = x[SM3_X86-151]
_ = x[SM4_X86-152]
_ = x[SPEC_CTRL_SSBD-153]
_ = x[SRBDS_CTRL-154]
_ = x[SRSO_MSR_FIX-155]
_ = x[SRSO_NO-156]
_ = x[SRSO_USER_KERNEL_NO-157]
_ = x[SSE-158]
_ = x[SSE2-159]
_ = x[SSE3-160]
_ = x[SSE4-161]
_ = x[SSE42-162]
_ = x[SSE4A-163]
_ = x[SSSE3-164]
_ = x[STIBP-165]
_ = x[STIBP_ALWAYSON-166]
_ = x[STOSB_SHORT-167]
_ = x[SUCCOR-168]
_ = x[SVM-169]
_ = x[SVMDA-170]
_ = x[SVMFBASID-171]
_ = x[SVML-172]
_ = x[SVMNP-173]
_ = x[SVMPF-174]
_ = x[SVMPFT-175]
_ = x[SYSCALL-176]
_ = x[SYSEE-177]
_ = x[TBM-178]
_ = x[TDX_GUEST-179]
_ = x[TLB_FLUSH_NESTED-180]
_ = x[TME-181]
_ = x[TOPEXT-182]
_ = x[TSA_L1_NO-183]
_ = x[TSA_SQ_NO-184]
_ = x[TSA_VERW_CLEAR-185]
_ = x[TSCRATEMSR-186]
_ = x[TSXLDTRK-187]
_ = x[VAES-188]
_ = x[VMCBCLEAN-189]
_ = x[VMPL-190]
_ = x[VMSA_REGPROT-191]
_ = x[VMX-192]
_ = x[VPCLMULQDQ-193]
_ = x[VTE-194]
_ = x[WAITPKG-195]
_ = x[WBNOINVD-196]
_ = x[WRMSRNS-197]
_ = x[X87-198]
_ = x[XGETBV1-199]
_ = x[XOP-200]
_ = x[XSAVE-201]
_ = x[XSAVEC-202]
_ = x[XSAVEOPT-203]
_ = x[XSAVES-204]
_ = x[AESARM-205]
_ = x[ARMCPUID-206]
_ = x[ASIMD-207]
_ = x[ASIMDDP-208]
_ = x[ASIMDHP-209]
_ = x[ASIMDRDM-210]
_ = x[ATOMICS-211]
_ = x[CRC32-212]
_ = x[DCPOP-213]
_ = x[EVTSTRM-214]
_ = x[FCMA-215]
_ = x[FHM-216]
_ = x[FP-217]
_ = x[FPHP-218]
_ = x[GPA-219]
_ = x[JSCVT-220]
_ = x[LRCPC-221]
_ = x[PMULL-222]
_ = x[RNDR-223]
_ = x[TLB-224]
_ = x[TS-225]
_ = x[SHA1-226]
_ = x[SHA2-227]
_ = x[SHA3-228]
_ = x[SHA512-229]
_ = x[SM3-230]
_ = x[SM4-231]
_ = x[SVE-232]
_ = x[PMU_FIXEDCOUNTER_CYCLES-233]
_ = x[PMU_FIXEDCOUNTER_REFCYCLES-234]
_ = x[PMU_FIXEDCOUNTER_INSTRUCTIONS-235]
_ = x[PMU_FIXEDCOUNTER_TOPDOWN_SLOTS-236]
_ = x[lastID-237]
_ = x[firstID-0]
}
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXFP16AMXINT8AMXFP8AMXTILEAMXTF32AMXCOMPLEXAMXTRANSPOSEAPX_FAVXAVX10AVX10_128AVX10_256AVX10_512AVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXIFMAAVXNECONVERTAVXSLOWAVXVNNIAVXVNNIINT8AVXVNNIINT16BHI_CTRLBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPCCXADDCMPSB_SCADBS_SHORTCMPXCHG8CPBOOSTCPPCCX16EFER_LMSLE_UNSENQCMDERMSF16CFLUSH_L1DFMA3FMA4FP128FP256FSRMFXSRFXSROPTGFNIHLEHRESETHTTHWAHYBRID_CPUHYPERVISORIA32_ARCH_CAPIA32_CORE_CAPIBPBIBPB_BRTYPEIBRSIBRS_PREFERREDIBRS_PROVIDES_SMPIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_FETCH_CTLXIBS_OPDATA4IBS_OPFUSEIBS_PREVENTHOSTIBS_ZEN4IDPRED_CTRLINT_WBINVDINVLPGBKEYLOCKERKEYLOCKERWLAHFLAMLBRVIRTLZCNTMCAOVERFLOWMCDT_NOMCOMMITMD_CLEARMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMOVSB_ZLMOVUMPXMSRIRCMSRLISTMSR_PAGEFLUSHNRIPSNXOSXSAVEPCONFIGPOPCNTPPINPREFETCHIPSFDRDPRURDRANDRDSEEDRDTSCPRRSBA_CTRLRTMRTM_ALWAYS_ABORTSBPBSERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSGXPQCSHASMESME_COHERENTSM3_X86SM4_X86SPEC_CTRL_SSBDSRBDS_CTRLSRSO_MSR_FIXSRSO_NOSRSO_USER_KERNEL_NOSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSTIBP_ALWAYSONSTOSB_SHORTSUCCORSVMSVMDASVMFBASIDSVMLSVMNPSVMPFSVMPFTSYSCALLSYSEETBMTDX_GUESTTLB_FLUSH_NESTEDTMETOPEXTTSA_L1_NOTSA_SQ_NOTSA_VERW_CLEARTSCRATEMSRTSXLDTRKVAESVMCBCLEANVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDWRMSRNSX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFHMFPFPHPGPAJSCVTLRCPCPMULLRNDRTLBTSSHA1SHA2SHA3SHA512SM3SM4SVEPMU_FIXEDCOUNTER_CYCLESPMU_FIXEDCOUNTER_REFCYCLESPMU_FIXEDCOUNTER_INSTRUCTIONSPMU_FIXEDCOUNTER_TOPDOWN_SLOTSlastID"
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 61, 68, 75, 85, 97, 102, 105, 110, 119, 128, 137, 141, 151, 163, 171, 179, 187, 195, 202, 212, 222, 230, 240, 251, 259, 269, 287, 302, 309, 321, 328, 335, 346, 358, 366, 370, 374, 380, 385, 393, 398, 404, 408, 417, 435, 443, 450, 454, 458, 472, 478, 482, 486, 495, 499, 503, 508, 513, 517, 521, 528, 532, 535, 541, 544, 547, 557, 567, 580, 593, 597, 608, 612, 626, 643, 646, 656, 667, 673, 681, 692, 700, 712, 728, 742, 753, 763, 778, 786, 797, 807, 814, 823, 833, 837, 840, 847, 852, 863, 870, 877, 885, 888, 894, 899, 908, 915, 923, 927, 930, 936, 943, 956, 961, 963, 970, 977, 983, 987, 996, 1000, 1005, 1011, 1017, 1023, 1033, 1036, 1052, 1056, 1065, 1068, 1077, 1092, 1105, 1111, 1125, 1132, 1135, 1140, 1146, 1149, 1152, 1164, 1171, 1178, 1192, 1202, 1214, 1221, 1240, 1243, 1247, 1251, 1255, 1260, 1265, 1270, 1275, 1289, 1300, 1306, 1309, 1314, 1323, 1327, 1332, 1337, 1343, 1350, 1355, 1358, 1367, 1383, 1386, 1392, 1401, 1410, 1424, 1434, 1442, 1446, 1455, 1459, 1471, 1474, 1484, 1487, 1494, 1502, 1509, 1512, 1519, 1522, 1527, 1533, 1541, 1547, 1553, 1561, 1566, 1573, 1580, 1588, 1595, 1600, 1605, 1612, 1616, 1619, 1621, 1625, 1628, 1633, 1638, 1643, 1647, 1650, 1652, 1656, 1660, 1664, 1670, 1673, 1676, 1679, 1702, 1728, 1757, 1787, 1793}
func (i FeatureID) String() string {
if i < 0 || i >= FeatureID(len(_FeatureID_index)-1) {
return "FeatureID(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _FeatureID_name[_FeatureID_index[i]:_FeatureID_index[i+1]]
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[VendorUnknown-0]
_ = x[Intel-1]
_ = x[AMD-2]
_ = x[VIA-3]
_ = x[Transmeta-4]
_ = x[NSC-5]
_ = x[KVM-6]
_ = x[MSVM-7]
_ = x[VMware-8]
_ = x[XenHVM-9]
_ = x[Bhyve-10]
_ = x[Hygon-11]
_ = x[SiS-12]
_ = x[RDC-13]
_ = x[Ampere-14]
_ = x[ARM-15]
_ = x[Broadcom-16]
_ = x[Cavium-17]
_ = x[DEC-18]
_ = x[Fujitsu-19]
_ = x[Infineon-20]
_ = x[Motorola-21]
_ = x[NVIDIA-22]
_ = x[AMCC-23]
_ = x[Qualcomm-24]
_ = x[Marvell-25]
_ = x[QEMU-26]
_ = x[QNX-27]
_ = x[ACRN-28]
_ = x[SRE-29]
_ = x[Apple-30]
_ = x[lastVendor-31]
}
const _Vendor_name = "VendorUnknownIntelAMDVIATransmetaNSCKVMMSVMVMwareXenHVMBhyveHygonSiSRDCAmpereARMBroadcomCaviumDECFujitsuInfineonMotorolaNVIDIAAMCCQualcommMarvellQEMUQNXACRNSREApplelastVendor"
var _Vendor_index = [...]uint8{0, 13, 18, 21, 24, 33, 36, 39, 43, 49, 55, 60, 65, 68, 71, 77, 80, 88, 94, 97, 104, 112, 120, 126, 130, 138, 145, 149, 152, 156, 159, 164, 174}
func (i Vendor) String() string {
if i < 0 || i >= Vendor(len(_Vendor_index)-1) {
return "Vendor(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Vendor_name[_Vendor_index[i]:_Vendor_index[i+1]]
}
-129
View File
@@ -1,129 +0,0 @@
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
package cpuid
import (
"runtime"
"strings"
"golang.org/x/sys/unix"
)
func detectOS(c *CPUInfo) bool {
if runtime.GOOS != "ios" {
tryToFillCPUInfoFomSysctl(c)
}
// There are no hw.optional sysctl values for the below features on Mac OS 11.0
// to detect their supported state dynamically. Assume the CPU features that
// Apple Silicon M1 supports to be available as a minimal set of features
// to all Go programs running on darwin/arm64.
// TODO: Add more if we know them.
c.featureSet.setIf(runtime.GOOS != "ios", AESARM, PMULL, SHA1, SHA2)
return true
}
func sysctlGetBool(name string) bool {
value, err := unix.SysctlUint32(name)
if err != nil {
return false
}
return value != 0
}
func sysctlGetString(name string) string {
value, err := unix.Sysctl(name)
if err != nil {
return ""
}
return value
}
func sysctlGetInt(unknown int, names ...string) int {
for _, name := range names {
value, err := unix.SysctlUint32(name)
if err != nil {
continue
}
if value != 0 {
return int(value)
}
}
return unknown
}
func sysctlGetInt64(unknown int, names ...string) int {
for _, name := range names {
value64, err := unix.SysctlUint64(name)
if err != nil {
continue
}
if int(value64) != unknown {
return int(value64)
}
}
return unknown
}
func setFeature(c *CPUInfo, feature FeatureID, aliases ...string) {
for _, alias := range aliases {
set := sysctlGetBool(alias)
c.featureSet.setIf(set, feature)
if set {
break
}
}
}
func tryToFillCPUInfoFomSysctl(c *CPUInfo) {
c.BrandName = sysctlGetString("machdep.cpu.brand_string")
if len(c.BrandName) != 0 {
c.VendorString = strings.Fields(c.BrandName)[0]
}
c.PhysicalCores = sysctlGetInt(runtime.NumCPU(), "hw.physicalcpu")
c.ThreadsPerCore = sysctlGetInt(1, "machdep.cpu.thread_count", "kern.num_threads") /
sysctlGetInt(1, "hw.physicalcpu")
c.LogicalCores = sysctlGetInt(runtime.NumCPU(), "machdep.cpu.core_count")
c.Family = sysctlGetInt(0, "machdep.cpu.family", "hw.cpufamily")
c.Model = sysctlGetInt(0, "machdep.cpu.model")
c.CacheLine = sysctlGetInt64(0, "hw.cachelinesize")
c.Cache.L1I = sysctlGetInt64(-1, "hw.l1icachesize")
c.Cache.L1D = sysctlGetInt64(-1, "hw.l1dcachesize")
c.Cache.L2 = sysctlGetInt64(-1, "hw.l2cachesize")
c.Cache.L3 = sysctlGetInt64(-1, "hw.l3cachesize")
// ARM features:
//
// Note: On some Apple Silicon system, some feats have aliases. See:
// https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics
// When so, we look at all aliases and consider a feature available when at least one identifier matches.
setFeature(c, AESARM, "hw.optional.arm.FEAT_AES") // AES instructions
setFeature(c, ASIMD, "hw.optional.arm.AdvSIMD", "hw.optional.neon") // Advanced SIMD
setFeature(c, ASIMDDP, "hw.optional.arm.FEAT_DotProd") // SIMD Dot Product
setFeature(c, ASIMDHP, "hw.optional.arm.AdvSIMD_HPFPCvt", "hw.optional.neon_hpfp") // Advanced SIMD half-precision floating point
setFeature(c, ASIMDRDM, "hw.optional.arm.FEAT_RDM") // Rounding Double Multiply Accumulate/Subtract
setFeature(c, ATOMICS, "hw.optional.arm.FEAT_LSE", "hw.optional.armv8_1_atomics") // Large System Extensions (LSE)
setFeature(c, CRC32, "hw.optional.arm.FEAT_CRC32", "hw.optional.armv8_crc32") // CRC32/CRC32C instructions
setFeature(c, DCPOP, "hw.optional.arm.FEAT_DPB") // Data cache clean to Point of Persistence (DC CVAP)
setFeature(c, EVTSTRM, "hw.optional.arm.FEAT_ECV") // Generic timer
setFeature(c, FCMA, "hw.optional.arm.FEAT_FCMA", "hw.optional.armv8_3_compnum") // Floating point complex number addition and multiplication
setFeature(c, FHM, "hw.optional.armv8_2_fhm", "hw.optional.arm.FEAT_FHM") // FMLAL and FMLSL instructions
setFeature(c, FP, "hw.optional.floatingpoint") // Single-precision and double-precision floating point
setFeature(c, FPHP, "hw.optional.arm.FEAT_FP16", "hw.optional.neon_fp16") // Half-precision floating point
setFeature(c, GPA, "hw.optional.arm.FEAT_PAuth") // Generic Pointer Authentication
setFeature(c, JSCVT, "hw.optional.arm.FEAT_JSCVT") // Javascript-style double->int convert (FJCVTZS)
setFeature(c, LRCPC, "hw.optional.arm.FEAT_LRCPC") // Weaker release consistency (LDAPR, etc)
setFeature(c, PMULL, "hw.optional.arm.FEAT_PMULL") // Polynomial Multiply instructions (PMULL/PMULL2)
setFeature(c, RNDR, "hw.optional.arm.FEAT_RNG") // Random Number instructions
setFeature(c, TLB, "hw.optional.arm.FEAT_TLBIOS", "hw.optional.arm.FEAT_TLBIRANGE") // Outer Shareable and TLB range maintenance instructions
setFeature(c, TS, "hw.optional.arm.FEAT_FlagM", "hw.optional.arm.FEAT_FlagM2") // Flag manipulation instructions
setFeature(c, SHA1, "hw.optional.arm.FEAT_SHA1") // SHA-1 instructions (SHA1C, etc)
setFeature(c, SHA2, "hw.optional.arm.FEAT_SHA256") // SHA-2 instructions (SHA256H, etc)
setFeature(c, SHA3, "hw.optional.arm.FEAT_SHA3") // SHA-3 instructions (EOR3, RAXI, XAR, BCAX)
setFeature(c, SHA512, "hw.optional.arm.FEAT_SHA512") // SHA512 instructions
setFeature(c, SM3, "hw.optional.arm.FEAT_SM3") // SM3 instructions
setFeature(c, SM4, "hw.optional.arm.FEAT_SM4") // SM4 instructions
setFeature(c, SVE, "hw.optional.arm.FEAT_SVE") // Scalable Vector Extension
}
-208
View File
@@ -1,208 +0,0 @@
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file located
// here https://github.com/golang/sys/blob/master/LICENSE
package cpuid
import (
"encoding/binary"
"io/ioutil"
"runtime"
)
// HWCAP bits.
const (
hwcap_FP = 1 << 0
hwcap_ASIMD = 1 << 1
hwcap_EVTSTRM = 1 << 2
hwcap_AES = 1 << 3
hwcap_PMULL = 1 << 4
hwcap_SHA1 = 1 << 5
hwcap_SHA2 = 1 << 6
hwcap_CRC32 = 1 << 7
hwcap_ATOMICS = 1 << 8
hwcap_FPHP = 1 << 9
hwcap_ASIMDHP = 1 << 10
hwcap_CPUID = 1 << 11
hwcap_ASIMDRDM = 1 << 12
hwcap_JSCVT = 1 << 13
hwcap_FCMA = 1 << 14
hwcap_LRCPC = 1 << 15
hwcap_DCPOP = 1 << 16
hwcap_SHA3 = 1 << 17
hwcap_SM3 = 1 << 18
hwcap_SM4 = 1 << 19
hwcap_ASIMDDP = 1 << 20
hwcap_SHA512 = 1 << 21
hwcap_SVE = 1 << 22
hwcap_ASIMDFHM = 1 << 23
hwcap_DIT = 1 << 24
hwcap_USCAT = 1 << 25
hwcap_ILRCPC = 1 << 26
hwcap_FLAGM = 1 << 27
hwcap_SSBS = 1 << 28
hwcap_SB = 1 << 29
hwcap_PACA = 1 << 30
hwcap_PACG = 1 << 31
hwcap_GCS = 1 << 32
hwcap2_DCPODP = 1 << 0
hwcap2_SVE2 = 1 << 1
hwcap2_SVEAES = 1 << 2
hwcap2_SVEPMULL = 1 << 3
hwcap2_SVEBITPERM = 1 << 4
hwcap2_SVESHA3 = 1 << 5
hwcap2_SVESM4 = 1 << 6
hwcap2_FLAGM2 = 1 << 7
hwcap2_FRINT = 1 << 8
hwcap2_SVEI8MM = 1 << 9
hwcap2_SVEF32MM = 1 << 10
hwcap2_SVEF64MM = 1 << 11
hwcap2_SVEBF16 = 1 << 12
hwcap2_I8MM = 1 << 13
hwcap2_BF16 = 1 << 14
hwcap2_DGH = 1 << 15
hwcap2_RNG = 1 << 16
hwcap2_BTI = 1 << 17
hwcap2_MTE = 1 << 18
hwcap2_ECV = 1 << 19
hwcap2_AFP = 1 << 20
hwcap2_RPRES = 1 << 21
hwcap2_MTE3 = 1 << 22
hwcap2_SME = 1 << 23
hwcap2_SME_I16I64 = 1 << 24
hwcap2_SME_F64F64 = 1 << 25
hwcap2_SME_I8I32 = 1 << 26
hwcap2_SME_F16F32 = 1 << 27
hwcap2_SME_B16F32 = 1 << 28
hwcap2_SME_F32F32 = 1 << 29
hwcap2_SME_FA64 = 1 << 30
hwcap2_WFXT = 1 << 31
hwcap2_EBF16 = 1 << 32
hwcap2_SVE_EBF16 = 1 << 33
hwcap2_CSSC = 1 << 34
hwcap2_RPRFM = 1 << 35
hwcap2_SVE2P1 = 1 << 36
hwcap2_SME2 = 1 << 37
hwcap2_SME2P1 = 1 << 38
hwcap2_SME_I16I32 = 1 << 39
hwcap2_SME_BI32I32 = 1 << 40
hwcap2_SME_B16B16 = 1 << 41
hwcap2_SME_F16F16 = 1 << 42
hwcap2_MOPS = 1 << 43
hwcap2_HBC = 1 << 44
hwcap2_SVE_B16B16 = 1 << 45
hwcap2_LRCPC3 = 1 << 46
hwcap2_LSE128 = 1 << 47
hwcap2_FPMR = 1 << 48
hwcap2_LUT = 1 << 49
hwcap2_FAMINMAX = 1 << 50
hwcap2_F8CVT = 1 << 51
hwcap2_F8FMA = 1 << 52
hwcap2_F8DP4 = 1 << 53
hwcap2_F8DP2 = 1 << 54
hwcap2_F8E4M3 = 1 << 55
hwcap2_F8E5M2 = 1 << 56
hwcap2_SME_LUTV2 = 1 << 57
hwcap2_SME_F8F16 = 1 << 58
hwcap2_SME_F8F32 = 1 << 59
hwcap2_SME_SF8FMA = 1 << 60
hwcap2_SME_SF8DP4 = 1 << 61
hwcap2_SME_SF8DP2 = 1 << 62
hwcap2_POE = 1 << 63
)
func detectOS(c *CPUInfo) bool {
// For now assuming no hyperthreading is reasonable.
c.LogicalCores = runtime.NumCPU()
c.PhysicalCores = c.LogicalCores
c.ThreadsPerCore = 1
if hwcap == 0 {
// We did not get values from the runtime.
// Try reading /proc/self/auxv
// From https://github.com/golang/sys
const (
_AT_HWCAP = 16
_AT_HWCAP2 = 26
uintSize = int(32 << (^uint(0) >> 63))
)
buf, err := ioutil.ReadFile("/proc/self/auxv")
if err != nil {
// e.g. on android /proc/self/auxv is not accessible, so silently
// ignore the error and leave Initialized = false. On some
// architectures (e.g. arm64) doinit() implements a fallback
// readout and will set Initialized = true again.
return false
}
bo := binary.LittleEndian
for len(buf) >= 2*(uintSize/8) {
var tag, val uint
switch uintSize {
case 32:
tag = uint(bo.Uint32(buf[0:]))
val = uint(bo.Uint32(buf[4:]))
buf = buf[8:]
case 64:
tag = uint(bo.Uint64(buf[0:]))
val = uint(bo.Uint64(buf[8:]))
buf = buf[16:]
}
switch tag {
case _AT_HWCAP:
hwcap = val
case _AT_HWCAP2:
// Not used
}
}
if hwcap == 0 {
return false
}
}
// HWCap was populated by the runtime from the auxiliary vector.
// Use HWCap information since reading aarch64 system registers
// is not supported in user space on older linux kernels.
c.featureSet.setIf(isSet(hwcap, hwcap_AES), AESARM)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMD), ASIMD)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDDP), ASIMDDP)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDHP), ASIMDHP)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDRDM), ASIMDRDM)
c.featureSet.setIf(isSet(hwcap, hwcap_CPUID), ARMCPUID)
c.featureSet.setIf(isSet(hwcap, hwcap_CRC32), CRC32)
c.featureSet.setIf(isSet(hwcap, hwcap_DCPOP), DCPOP)
c.featureSet.setIf(isSet(hwcap, hwcap_EVTSTRM), EVTSTRM)
c.featureSet.setIf(isSet(hwcap, hwcap_FCMA), FCMA)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDFHM), FHM)
c.featureSet.setIf(isSet(hwcap, hwcap_FP), FP)
c.featureSet.setIf(isSet(hwcap, hwcap_FPHP), FPHP)
c.featureSet.setIf(isSet(hwcap, hwcap_JSCVT), JSCVT)
c.featureSet.setIf(isSet(hwcap, hwcap_LRCPC), LRCPC)
c.featureSet.setIf(isSet(hwcap, hwcap_PMULL), PMULL)
c.featureSet.setIf(isSet(hwcap, hwcap2_RNG), RNDR)
// c.featureSet.setIf(isSet(hwcap, hwcap_), TLB)
// c.featureSet.setIf(isSet(hwcap, hwcap_), TS)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA1), SHA1)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA2), SHA2)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA3), SHA3)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA512), SHA512)
c.featureSet.setIf(isSet(hwcap, hwcap_SM3), SM3)
c.featureSet.setIf(isSet(hwcap, hwcap_SM4), SM4)
c.featureSet.setIf(isSet(hwcap, hwcap_SVE), SVE)
// The Samsung S9+ kernel reports support for atomics, but not all cores
// actually support them, resulting in SIGILL. See issue #28431.
// TODO(elias.naur): Only disable the optimization on bad chipsets on android.
c.featureSet.setIf(isSet(hwcap, hwcap_ATOMICS) && runtime.GOOS != "android", ATOMICS)
return true
}
func isSet(hwc uint, value uint) bool {
return hwc&value != 0
}
-16
View File
@@ -1,16 +0,0 @@
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
//go:build arm64 && !linux && !darwin
// +build arm64,!linux,!darwin
package cpuid
import "runtime"
func detectOS(c *CPUInfo) bool {
c.PhysicalCores = runtime.NumCPU()
// For now assuming 1 thread per core...
c.ThreadsPerCore = 1
c.LogicalCores = c.PhysicalCores
return false
}
-8
View File
@@ -1,8 +0,0 @@
// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file.
//go:build nounsafe
// +build nounsafe
package cpuid
var hwcap uint
-11
View File
@@ -1,11 +0,0 @@
// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file.
//go:build !nounsafe
// +build !nounsafe
package cpuid
import _ "unsafe" // needed for go:linkname
//go:linkname hwcap internal/cpu.HWCap
var hwcap uint
-15
View File
@@ -1,15 +0,0 @@
#!/bin/sh
set -e
go tool dist list | while IFS=/ read os arch; do
echo "Checking $os/$arch..."
echo " normal"
GOARCH=$arch GOOS=$os go build -o /dev/null .
echo " noasm"
GOARCH=$arch GOOS=$os go build -tags noasm -o /dev/null .
echo " appengine"
GOARCH=$arch GOOS=$os go build -tags appengine -o /dev/null .
echo " noasm,appengine"
GOARCH=$arch GOOS=$os go build -tags 'appengine noasm' -o /dev/null .
done
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.27@sha256:512690a5660563b57d37ecc31129e7f136e831db2aed24a1dbeb8ad7380dc0fa
FROM golang:1.27@sha256:e432b43af23a9328d56a7c499be0476810aa344acbcf65fc7c455d4ff5a40602
ENV GOOS=linux
ENV GOARCH=arm
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.27@sha256:512690a5660563b57d37ecc31129e7f136e831db2aed24a1dbeb8ad7380dc0fa
FROM golang:1.27@sha256:e432b43af23a9328d56a7c499be0476810aa344acbcf65fc7c455d4ff5a40602
ENV GOOS=linux
ENV GOARCH=arm64
+23 -4
View File
@@ -1,18 +1,35 @@
FUZZ_TIME ?= 1m
FUZZ_FLAGS ?=
# The fuzz targets of each package, along with the tags needed to build them.
FUZZ_TARGETS = ./test/:gofuzz .:sha1cd_asmtest
export CGO_ENABLED := 1
# The hashing tests run a second time with SHA-NI off, so that CPUs with it
# also cover the AVX2 fallback that CPUs without it use.
.PHONY: test
test:
go test -race -timeout 15s ./...
go test -race -timeout 15s -tags sha1cd_asmtest ./...
SHA1CD_TEST_NOSHANI=1 go test -race -timeout 15s -tags sha1cd_asmtest ./test/
.PHONY: bench
bench:
go test -benchmem -run=^$$ -bench ^Benchmark ./...
# go test only fuzzes a single target per invocation, so each one is run in
# turn for FUZZ_TIME.
.PHONY: fuzz
fuzz:
go test -tags gofuzz -fuzz=. -fuzztime=$(FUZZ_TIME) ./test/
@set -e; for entry in $(FUZZ_TARGETS); do \
pkg="$${entry%%:*}"; tags="$${entry##*:}"; \
listed="$$(go test -tags "$$tags" -list '^Fuzz' "$$pkg")"; \
for target in $$(echo "$$listed" | grep '^Fuzz'); do \
echo "fuzzing $$target in $$pkg for $(FUZZ_TIME)"; \
go test $(FUZZ_FLAGS) -tags "$$tags" -run '^$$' -fuzz "^$$target"'$$' \
-fuzztime=$(FUZZ_TIME) "$$pkg"; \
done; \
done
# Cross build project in arm/v7.
build-arm:
@@ -24,9 +41,11 @@ build-arm64:
docker build -t sha1cd-arm64 -f Dockerfile.arm64 .
docker run --rm sha1cd-arm64
# Build with cgo disabled.
# Build with cgo disabled. Vetting every package (not just ./cgo) is what
# catches the cgo and non-cgo builds drifting apart in their exported API.
build-nocgo:
CGO_ENABLED=0 go build ./cgo
CGO_ENABLED=0 go build ./...
CGO_ENABLED=0 go vet ./...
# Run cross-compilation to assure supported architectures.
cross-build: build-arm build-arm64 build-nocgo
+28
View File
@@ -0,0 +1,28 @@
// Package cpu detects the CPU features that sha1cd dispatches on.
//
// It covers only what the assembly implementations need, which keeps
// start up cheap and avoids an external dependency. Every flag is false
// where a feature cannot be detected safely, which selects the generic
// implementation.
package cpu
// X86 holds the features of the current amd64 CPU. All flags are false on
// other architectures.
var X86 struct {
// HasAVX and HasAVX2 are set only when the OS also preserves the YMM
// state, and HasAVX512F only when it preserves the ZMM and opmask state.
HasAVX bool
HasAVX2 bool
HasAVX512F bool
HasBMI1 bool
HasBMI2 bool
HasSHA bool
HasSSSE3 bool
HasSSE41 bool
}
// ARM64 holds the features of the current arm64 CPU. All flags are false on
// other architectures.
var ARM64 struct {
HasSHA1 bool
}
+63
View File
@@ -0,0 +1,63 @@
//go:build !noasm && gc && amd64
package cpu
// cpuid and xgetbv are implemented in cpu_amd64.s.
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
func xgetbv() (eax, edx uint32)
func init() {
const (
// CPUID EAX=1: ECX
ssse3 = 1 << 9
sse41 = 1 << 19
osxsave = 1 << 27
avx = 1 << 28
// CPUID EAX=7, ECX=0: EBX
bmi1 = 1 << 3
avx2 = 1 << 5
bmi2 = 1 << 8
avx512f = 1 << 16
sha = 1 << 29
// XCR0
xmmState = 1 << 1
ymmState = 1 << 2
opmaskState = 1 << 5
zmmHi256State = 1 << 6
hi16ZMMState = 1 << 7
zmmState = opmaskState | zmmHi256State | hi16ZMMState
)
maxID, _, _, _ := cpuid(0, 0)
if maxID < 1 {
return
}
_, _, ecx1, _ := cpuid(1, 0)
X86.HasSSSE3 = ecx1&ssse3 != 0
X86.HasSSE41 = ecx1&sse41 != 0
// VEX encoded instructions also need the OS to preserve the YMM state,
// which XGETBV reports once OSXSAVE says it is available.
// macOS enables the ZMM state lazily, so XCR0 may not report it and
// AVX-512 is then left unused, which is safe.
var osYMM, osZMM bool
if ecx1&(osxsave|avx) == osxsave|avx {
xcr0, _ := xgetbv()
osYMM = xcr0&(xmmState|ymmState) == xmmState|ymmState
osZMM = osYMM && xcr0&zmmState == zmmState
}
X86.HasAVX = osYMM
if maxID < 7 {
return
}
_, ebx7, _, _ := cpuid(7, 0)
X86.HasAVX2 = osYMM && ebx7&avx2 != 0
X86.HasAVX512F = osZMM && ebx7&avx512f != 0
X86.HasBMI1 = ebx7&bmi1 != 0
X86.HasBMI2 = ebx7&bmi2 != 0
X86.HasSHA = ebx7&sha != 0
}
+22
View File
@@ -0,0 +1,22 @@
//go:build !noasm && gc && amd64
#include "textflag.h"
// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
TEXT ·cpuid(SB), NOSPLIT, $0-24
MOVL eaxArg+0(FP), AX
MOVL ecxArg+4(FP), CX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func xgetbv() (eax, edx uint32)
TEXT ·xgetbv(SB), NOSPLIT, $0-8
MOVL $0, CX
XGETBV
MOVL AX, eax+0(FP)
MOVL DX, edx+4(FP)
RET
+9
View File
@@ -0,0 +1,9 @@
//go:build !noasm && gc && arm64 && darwin
package cpu
// Every Apple arm64 chip implements the ARMv8 Cryptographic Extension. The Go
// runtime makes the same assumption for crypto/sha1 on darwin/arm64.
func init() {
ARM64.HasSHA1 = true
}
+12
View File
@@ -0,0 +1,12 @@
//go:build !noasm && gc && arm64 && freebsd
package cpu
// getisar0 is implemented in cpu_arm64_freebsd.s.
func getisar0() uint64
// FreeBSD emulates user space reads of ID_AA64ISAR0_EL1. Its SHA1 field, bits
// [11:8], is non zero when the SHA1 instructions are implemented.
func init() {
ARM64.HasSHA1 = (getisar0()>>8)&0xf != 0
}
+9
View File
@@ -0,0 +1,9 @@
//go:build !noasm && gc && arm64 && freebsd
#include "textflag.h"
// func getisar0() uint64
TEXT ·getisar0(SB), NOSPLIT, $0-8
MRS ID_AA64ISAR0_EL1, R0
MOVD R0, ret+0(FP)
RET
+29
View File
@@ -0,0 +1,29 @@
//go:build !noasm && gc && arm64 && linux
package cpu
import (
"os"
_ "unsafe" // for go:linkname
)
// runtime_getAuxv returns the auxiliary vector the kernel passed to the
// process. The runtime keeps it reachable for golang.org/x/sys/cpu and
// others, see go.dev/issue/57336 and go.dev/issue/67401.
//
//go:linkname runtime_getAuxv runtime.getAuxv
func runtime_getAuxv() []uintptr
// Linux, and so Android, reports the SHA1 instructions through HWCAP, as
// not every kernel lets user space read the ID registers.
func init() {
hwcap, ok := hwcapFromAuxv(runtime_getAuxv())
if !ok {
// The procfs copy may not be readable in restricted environments,
// in which case the generic implementation is used.
if buf, err := os.ReadFile("/proc/self/auxv"); err == nil {
hwcap, _ = hwcapFromProcAuxv(buf)
}
}
ARM64.HasSHA1 = hwcap&hwcapSHA1 != 0
}
+18
View File
@@ -0,0 +1,18 @@
//go:build !noasm && gc && arm64 && windows
package cpu
import "syscall"
// Windows reports the ARMv8 Cryptographic Extension, which includes the SHA1
// instructions, through IsProcessorFeaturePresent.
func init() {
const _PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30
proc := syscall.NewLazyDLL("kernel32.dll").NewProc("IsProcessorFeaturePresent")
if proc.Find() != nil {
return
}
ret, _, _ := proc.Call(_PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)
ARM64.HasSHA1 = ret != 0
}
+32
View File
@@ -0,0 +1,32 @@
package cpu
import "encoding/binary"
const (
_AT_HWCAP = 16
// hwcapSHA1 is HWCAP_SHA1 on linux/arm64.
hwcapSHA1 = 1 << 5
)
// hwcapFromAuxv returns AT_HWCAP from an auxiliary vector of tag and value
// pairs, and whether it was present.
func hwcapFromAuxv(auxv []uintptr) (uint64, bool) {
for i := 0; i+1 < len(auxv); i += 2 {
if auxv[i] == _AT_HWCAP {
return uint64(auxv[i+1]), true
}
}
return 0, false
}
// hwcapFromProcAuxv does the same for the contents of /proc/self/auxv on a
// 64-bit little endian system.
func hwcapFromProcAuxv(buf []byte) (uint64, bool) {
for ; len(buf) >= 16; buf = buf[16:] {
if binary.LittleEndian.Uint64(buf) == _AT_HWCAP {
return binary.LittleEndian.Uint64(buf[8:]), true
}
}
return 0, false
}
+28 -18
View File
@@ -4,30 +4,39 @@
package sha1cd
import (
"runtime"
"github.com/klauspost/cpuid/v2"
shared "github.com/pjbgf/sha1cd/internal"
"github.com/pjbgf/sha1cd/internal/cpu"
"github.com/pjbgf/sha1cd/ubc"
)
var hasSHANI = (runtime.GOARCH == "amd64" &&
cpuid.CPU.Supports(cpuid.AVX) &&
cpuid.CPU.Supports(cpuid.SHA) &&
cpuid.CPU.Supports(cpuid.SSE3) &&
cpuid.CPU.Supports(cpuid.SSE4))
// hasSHANI reports whether blockAMD64 can run. It uses legacy SSE encodings
// only, so it does not need AVX and also runs on the Goldmont, Goldmont Plus
// and Tremont Atoms, which implement SHA-NI without AVX.
var hasSHANI = cpu.X86.HasSHA && cpu.X86.HasSSSE3 && cpu.X86.HasSSE41
// blockAMD64 hashes the message p into the current state in h.
// blockAMD64 hashes a single chunk of p into the current state in h.
// p must hold at least one whole chunk. Anything beyond the first chunk is
// ignored, as the collision detection the caller runs afterwards inspects m1
// and cs for one chunk only.
// Both m1 and cs are used to store intermediate results which are used by the collision detection logic.
//
//go:noescape
func blockAMD64(h []uint32, p []byte, m1 []uint32, cs [][5]uint32)
func block(dig *digest, p []byte) {
if forceGeneric || !hasSHANI {
switch {
case forceGeneric:
blockGeneric(dig, p)
case hasSHANI:
blockSHANI(dig, p)
case hasAVX2:
blockAVX2(dig, p)
default:
blockGeneric(dig, p)
return
}
}
func blockSHANI(dig *digest, p []byte) {
m1 := [shared.Rounds]uint32{}
cs := [shared.PreStepState][shared.WordBuffers]uint32{}
@@ -37,14 +46,15 @@ func block(dig *digest, p []byte) {
chunk := p[:shared.Chunk]
blockAMD64(dig.h[:], chunk, m1[:], cs[:])
rectifyCompressionState(&m1, &cs)
// Assembly states need repair only when a disturbance vector survives.
if mask := ubc.CalculateDvMask(&m1); mask != 0 {
rectifyCompressionState(&m1, &cs)
if checkCollision(&m1, &cs, &dig.h, mask) {
dig.col = true
col := checkCollision(&m1, &cs, &dig.h)
if col {
dig.col = true
blockAMD64(dig.h[:], chunk, m1[:], cs[:])
blockAMD64(dig.h[:], chunk, m1[:], cs[:])
blockAMD64(dig.h[:], chunk, m1[:], cs[:])
blockAMD64(dig.h[:], chunk, m1[:], cs[:])
}
}
p = p[shared.Chunk:]
+44 -49
View File
@@ -12,27 +12,30 @@
// - https://github.com/golang/go/blob/master/src/crypto/sha1/sha1block_amd64.s
// Reverse the dword order in abcd via PSHUFD then store the 16 bytes in one
// move, instead of issuing four VPEXTRD's that each go through the store port.
// move, instead of issuing four PEXTRD's that each go through the store port.
#define LOADCS(abcd, e, index, target) \
VPSHUFD $0x1B, abcd, X8; \
VMOVDQU X8, ((index*20)+0)(target); \
PSHUFD $0x1B, abcd, X8; \
MOVOU X8, ((index*20)+0)(target); \
MOVL e, ((index*20)+16)(target);
#define LOADM1(m1, index, target) \
VPSHUFD $0x1B, m1, X8; \
VMOVDQU X8, ((index*16)+0)(target);
PSHUFD $0x1B, m1, X8; \
MOVOU X8, ((index*16)+0)(target);
// func blockAMD64(h []uint32, p []byte, m1 []uint32, cs [][5]uint32)
// Requires: AVX, SHA, SSE2, SSE4.1, SSSE3
// Requires: SHA, SSE2, SSE4.1, SSSE3
TEXT ·blockAMD64(SB), NOSPLIT, $80-96
MOVQ h_base+0(FP), DI
MOVQ p_base+24(FP), SI
MOVQ p_len+32(FP), DX
MOVQ m1_base+48(FP), R13
MOVQ cs_base+72(FP), R15
CMPQ DX, $0x00
JEQ done
ADDQ SI, DX
// Truncate the length to whole chunks and skip the block if none is left.
// The caller compresses exactly one chunk per call, so that the collision
// detection it runs afterwards sees this chunk's m1 and cs.
ANDQ $-64, DX
JZ done
// Allocate space on the stack for saving ABCD and E0, and align it to 16 bytes
LEAQ 15(SP), AX
@@ -42,52 +45,51 @@ TEXT ·blockAMD64(SB), NOSPLIT, $80-96
// Load initial hash state
PINSRD $0x03, 16(DI), X5
VMOVDQU (DI), X0
MOVOU (DI), X0
PAND upper_mask<>+0(SB), X5
PSHUFD $0x1b, X0, X0
VMOVDQA shuffle_mask<>+0(SB), X7
MOVO shuffle_mask<>+0(SB), X7
loop:
// Save ABCD and E working values
VMOVDQA X5, (AX)
VMOVDQA X0, 16(AX)
MOVO X5, (AX)
MOVO X0, 16(AX)
// LOAD CS 0
VPEXTRD $3, X5, R12
PEXTRD $3, X5, R12
LOADCS(X0, R12, 0, R15)
// Rounds 0-3
VMOVDQU (SI), X1
MOVOU (SI), X1
PSHUFB X7, X1
PADDD X1, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1RNDS4 $0x00, X5, X0
LOADM1(X1, 0, R13)
// Rounds 4-7
VMOVDQU 16(SI), X2
MOVOU 16(SI), X2
PSHUFB X7, X2
SHA1NEXTE X2, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1RNDS4 $0x00, X6, X0
SHA1MSG1 X2, X1
LOADM1(X2, 1, R13)
// Rounds 8-11
VMOVDQU 32(SI), X3
MOVOU 32(SI), X3
PSHUFB X7, X3
SHA1NEXTE X3, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1RNDS4 $0x00, X5, X0
SHA1MSG1 X3, X2
PXOR X3, X1
LOADM1(X3, 2, R13)
// Rounds 12-15
VMOVDQU 48(SI), X4
MOVOU 48(SI), X4
PSHUFB X7, X4
SHA1NEXTE X4, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X4, X1
SHA1RNDS4 $0x00, X6, X0
SHA1MSG1 X4, X3
@@ -96,7 +98,7 @@ loop:
// Rounds 16-19
SHA1NEXTE X1, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X1, X2
SHA1RNDS4 $0x00, X5, X0
SHA1MSG1 X1, X4
@@ -105,7 +107,7 @@ loop:
// Rounds 20-23
SHA1NEXTE X2, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X2, X3
SHA1RNDS4 $0x01, X6, X0
SHA1MSG1 X2, X1
@@ -114,7 +116,7 @@ loop:
// Rounds 24-27
SHA1NEXTE X3, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X3, X4
SHA1RNDS4 $0x01, X5, X0
SHA1MSG1 X3, X2
@@ -123,7 +125,7 @@ loop:
// Rounds 28-31
SHA1NEXTE X4, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X4, X1
SHA1RNDS4 $0x01, X6, X0
SHA1MSG1 X4, X3
@@ -132,7 +134,7 @@ loop:
// Rounds 32-35
SHA1NEXTE X1, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X1, X2
SHA1RNDS4 $0x01, X5, X0
SHA1MSG1 X1, X4
@@ -141,7 +143,7 @@ loop:
// Rounds 36-39
SHA1NEXTE X2, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X2, X3
SHA1RNDS4 $0x01, X6, X0
SHA1MSG1 X2, X1
@@ -150,7 +152,7 @@ loop:
// Rounds 40-43
SHA1NEXTE X3, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X3, X4
SHA1RNDS4 $0x02, X5, X0
SHA1MSG1 X3, X2
@@ -159,7 +161,7 @@ loop:
// Rounds 44-47
SHA1NEXTE X4, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X4, X1
SHA1RNDS4 $0x02, X6, X0
SHA1MSG1 X4, X3
@@ -168,21 +170,20 @@ loop:
// Rounds 48-51
SHA1NEXTE X1, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X1, X2
SHA1RNDS4 $0x02, X5, X0
VPEXTRD $0, X5, R12
SHA1MSG1 X1, X4
PXOR X1, X3
LOADM1(X1, 12, R13)
// derive pre-round 56's E out of round 51's A.
VPEXTRD $3, X0, R12
PEXTRD $3, X0, R12
ROLL $30, R12
// Rounds 52-55
SHA1NEXTE X2, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X2, X3
SHA1RNDS4 $0x02, X6, X0
SHA1MSG1 X2, X1
@@ -194,21 +195,20 @@ loop:
// Rounds 56-59
SHA1NEXTE X3, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X3, X4
SHA1RNDS4 $0x02, X5, X0
VPEXTRD $0, X5, R12
SHA1MSG1 X3, X2
PXOR X3, X1
LOADM1(X3, 14, R13)
// derive pre-round 64's E out of round 59's A.
VPEXTRD $3, X0, R12
PEXTRD $3, X0, R12
ROLL $30, R12
// Rounds 60-63
SHA1NEXTE X4, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X4, X1
SHA1RNDS4 $0x03, X6, X0
SHA1MSG1 X4, X3
@@ -220,7 +220,7 @@ loop:
// Rounds 64-67
SHA1NEXTE X1, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X1, X2
SHA1RNDS4 $0x03, X5, X0
SHA1MSG1 X1, X4
@@ -229,7 +229,7 @@ loop:
// Rounds 68-71
SHA1NEXTE X2, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X2, X3
SHA1RNDS4 $0x03, X6, X0
PXOR X2, X4
@@ -237,14 +237,14 @@ loop:
// Rounds 72-75
SHA1NEXTE X3, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X3, X4
SHA1RNDS4 $0x03, X5, X0
LOADM1(X3, 18, R13)
// Rounds 76-79
SHA1NEXTE X4, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1RNDS4 $0x03, X6, X0
LOADM1(X4, 19, R13)
@@ -252,14 +252,9 @@ loop:
SHA1NEXTE (AX), X5
PADDD 16(AX), X0
// Check if we are done, if not return to the loop
ADDQ $0x40, SI
CMPQ SI, DX
JNE loop
// Write the hash state back to digest
PSHUFD $0x1b, X0, X0
VMOVDQU X0, (DI)
MOVOU X0, (DI)
PEXTRD $0x03, X5, 16(DI)
done:
+15 -11
View File
@@ -4,15 +4,17 @@
package sha1cd
import (
"runtime"
"github.com/klauspost/cpuid/v2"
shared "github.com/pjbgf/sha1cd/internal"
"github.com/pjbgf/sha1cd/internal/cpu"
"github.com/pjbgf/sha1cd/ubc"
)
var hasSHA1 = (runtime.GOARCH == "arm64" && cpuid.CPU.Supports(cpuid.SHA1))
var hasSHA1 = cpu.ARM64.HasSHA1
// blockARM64 hashes the message p into the current state in h.
// blockARM64 hashes a single chunk of p into the current state in h.
// p must hold at least one whole chunk. Anything beyond the first chunk is
// ignored, as the collision detection the caller runs afterwards inspects m1
// and cs for one chunk only.
// Both m1 and cs are used to store intermediate results which are used by the collision detection logic.
//
//go:noescape
@@ -34,13 +36,15 @@ func block(dig *digest, p []byte) {
blockARM64(dig.h[:], chunk, m1[:], cs[:])
rectifyCompressionState(&m1, &cs)
col := checkCollision(&m1, &cs, &dig.h)
if col {
dig.col = true
// Assembly states need repair only when a disturbance vector survives.
if mask := ubc.CalculateDvMask(&m1); mask != 0 {
rectifyCompressionState(&m1, &cs)
if checkCollision(&m1, &cs, &dig.h, mask) {
dig.col = true
blockARM64(dig.h[:], chunk, m1[:], cs[:])
blockARM64(dig.h[:], chunk, m1[:], cs[:])
blockARM64(dig.h[:], chunk, m1[:], cs[:])
blockARM64(dig.h[:], chunk, m1[:], cs[:])
}
}
p = p[shared.Chunk:]
+2 -4
View File
@@ -37,15 +37,13 @@ TEXT ·blockARM64(SB), NOSPLIT, $80-96
LSR $6, R2, R2
LSL $6, R2, R2
ADD R16, R2, R21
VLD1.P 16(R0), [V0.S4]
FMOVS (R0), F20
SUB $16, R0, R0
loop:
CMP R16, R21
BLS end
// The caller passes exactly one block; skip hashing only if p is shorter.
CBZ R2, end
// Load block (p) into 16-bytes vectors.
VLD1.P 16(R1), [V4.B16]
+100
View File
@@ -0,0 +1,100 @@
//go:build !noasm && gc && arm64 && !amd64 && sha1cd_asmtest
#include "textflag.h"
// callBlockARM64DirtyRegs calls blockARM64 with every general-purpose and
// vector register the caller does not own set to all ones. It is only built
// with the sha1cd_asmtest tag, to catch the assembly reading a register before
// writing it.
//
// func callBlockARM64DirtyRegs(h []uint32, p []byte, m1 []uint32, cs [][5]uint32)
TEXT ·callBlockARM64DirtyRegs(SB), NOSPLIT, $104-96
MOVD h_base+0(FP), R0
MOVD R0, 8(RSP)
MOVD h_len+8(FP), R0
MOVD R0, 16(RSP)
MOVD h_cap+16(FP), R0
MOVD R0, 24(RSP)
MOVD p_base+24(FP), R0
MOVD R0, 32(RSP)
MOVD p_len+32(FP), R0
MOVD R0, 40(RSP)
MOVD p_cap+40(FP), R0
MOVD R0, 48(RSP)
MOVD m1_base+48(FP), R0
MOVD R0, 56(RSP)
MOVD m1_len+56(FP), R0
MOVD R0, 64(RSP)
MOVD m1_cap+64(FP), R0
MOVD R0, 72(RSP)
MOVD cs_base+72(FP), R0
MOVD R0, 80(RSP)
MOVD cs_len+80(FP), R0
MOVD R0, 88(RSP)
MOVD cs_cap+88(FP), R0
MOVD R0, 96(RSP)
// R18 is reserved by the platform, R27 by the assembler, R28 holds g,
// R29 is the frame pointer and R30 the link register.
MOVD $-1, R0
MOVD R0, R1
MOVD R0, R2
MOVD R0, R3
MOVD R0, R4
MOVD R0, R5
MOVD R0, R6
MOVD R0, R7
MOVD R0, R8
MOVD R0, R9
MOVD R0, R10
MOVD R0, R11
MOVD R0, R12
MOVD R0, R13
MOVD R0, R14
MOVD R0, R15
MOVD R0, R16
MOVD R0, R17
MOVD R0, R19
MOVD R0, R20
MOVD R0, R21
MOVD R0, R22
MOVD R0, R23
MOVD R0, R24
MOVD R0, R25
MOVD R0, R26
VMOVI $0xff, V0.B16
VMOVI $0xff, V1.B16
VMOVI $0xff, V2.B16
VMOVI $0xff, V3.B16
VMOVI $0xff, V4.B16
VMOVI $0xff, V5.B16
VMOVI $0xff, V6.B16
VMOVI $0xff, V7.B16
VMOVI $0xff, V8.B16
VMOVI $0xff, V9.B16
VMOVI $0xff, V10.B16
VMOVI $0xff, V11.B16
VMOVI $0xff, V12.B16
VMOVI $0xff, V13.B16
VMOVI $0xff, V14.B16
VMOVI $0xff, V15.B16
VMOVI $0xff, V16.B16
VMOVI $0xff, V17.B16
VMOVI $0xff, V18.B16
VMOVI $0xff, V19.B16
VMOVI $0xff, V20.B16
VMOVI $0xff, V21.B16
VMOVI $0xff, V22.B16
VMOVI $0xff, V23.B16
VMOVI $0xff, V24.B16
VMOVI $0xff, V25.B16
VMOVI $0xff, V26.B16
VMOVI $0xff, V27.B16
VMOVI $0xff, V28.B16
VMOVI $0xff, V29.B16
VMOVI $0xff, V30.B16
VMOVI $0xff, V31.B16
BL ·blockARM64(SB)
RET
+55
View File
@@ -0,0 +1,55 @@
//go:build !noasm && gc && amd64
package sha1cd
import (
shared "github.com/pjbgf/sha1cd/internal"
"github.com/pjbgf/sha1cd/internal/cpu"
"github.com/pjbgf/sha1cd/ubc"
)
// hasAVX2 reports whether blockAVX2 can run. It is the fallback for CPUs
// without SHA-NI, such as Intel's big cores before Ice Lake.
var hasAVX2 = cpu.X86.HasAVX2 && cpu.X86.HasBMI1 && cpu.X86.HasBMI2
// scheduleAVX2 expands the message schedules of the blocks at pa and pb, which
// may be the same, into m1a and m1b.
//
//go:noescape
func scheduleAVX2(pa, pb *byte, m1a, m1b *[shared.Rounds]uint32)
// roundsBMI2 compresses the block whose schedule is in m1 into h, and stores
// the states before steps 0, 58 and 65 into cs.
//
//go:noescape
func roundsBMI2(h *[shared.WordBuffers]uint32, m1 *[shared.Rounds]uint32,
cs *[shared.PreStepState][shared.WordBuffers]uint32)
func blockAVX2(dig *digest, p []byte) {
var m1 [2][shared.Rounds]uint32
cs := [shared.PreStepState][shared.WordBuffers]uint32{}
for len(p) >= shared.Chunk {
// The schedules do not depend on the chaining state, so expand two
// blocks at once. The rounds must still run one block at a time, as a
// detected collision changes the state the next block starts from.
n, pb := 1, p
if len(p) >= 2*shared.Chunk {
n, pb = 2, p[shared.Chunk:]
}
scheduleAVX2(&p[0], &pb[0], &m1[0], &m1[1])
for j := 0; j < n; j++ {
w := &m1[j]
roundsBMI2(&dig.h, w, &cs)
if mask := ubc.CalculateDvMask(w); mask != 0 && checkCollision(w, &cs, &dig.h, mask) {
dig.col = true
roundsBMI2(&dig.h, w, &cs)
roundsBMI2(&dig.h, w, &cs)
}
}
p = p[n*shared.Chunk:]
}
}
+415
View File
@@ -0,0 +1,415 @@
//go:build !noasm && gc && amd64
#include "textflag.h"
// The fallback for CPUs without SHA-NI. The message schedule is expanded with
// AVX2 for two blocks at a time, one per 128-bit lane, as it does not depend
// on the chaining state. The rounds then run one block at a time with scalar
// BMI instructions, so that the collision detection can run between blocks
// and gets the exact states before steps 58 and 65, with no repair needed.
// func scheduleAVX2(pa, pb *byte, m1a, m1b *[80]uint32)
// Requires: AVX, AVX2
//
// Expands the blocks at pa and pb, which may be the same, into m1a and m1b.
// Y0-Y7 hold the last eight groups of four words, group g in Y(g%8).
TEXT ·scheduleAVX2(SB), NOSPLIT, $0-32
MOVQ pa+0(FP), AX
MOVQ pb+8(FP), BX
MOVQ m1a+16(FP), CX
MOVQ m1b+24(FP), DX
VBROADCASTI128 bswap_mask<>(SB), Y13
// W[0..3] loaded from the blocks
VMOVDQU 0(AX), X0
VINSERTI128 $1, 0(BX), Y0, Y0
VPSHUFB Y13, Y0, Y0
VMOVDQU X0, 0(CX)
VEXTRACTI128 $1, Y0, 0(DX)
// W[4..7] loaded from the blocks
VMOVDQU 16(AX), X1
VINSERTI128 $1, 16(BX), Y1, Y1
VPSHUFB Y13, Y1, Y1
VMOVDQU X1, 16(CX)
VEXTRACTI128 $1, Y1, 16(DX)
// W[8..11] loaded from the blocks
VMOVDQU 32(AX), X2
VINSERTI128 $1, 32(BX), Y2, Y2
VPSHUFB Y13, Y2, Y2
VMOVDQU X2, 32(CX)
VEXTRACTI128 $1, Y2, 32(DX)
// W[12..15] loaded from the blocks
VMOVDQU 48(AX), X3
VINSERTI128 $1, 48(BX), Y3, Y3
VPSHUFB Y13, Y3, Y3
VMOVDQU X3, 48(CX)
VEXTRACTI128 $1, Y3, 48(DX)
// W[16..19] = rol1(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]). W[i+3] needs
// W[i] from this group, which is added in afterwards.
VPALIGNR $8, Y0, Y1, Y8 // W[i-14..i-11]
VPXOR Y0, Y8, Y8 // W[i-16..i-13]
VPXOR Y2, Y8, Y8 // W[i-8..i-5]
VPSRLDQ $4, Y3, Y9 // W[i-3..i-1], 0
VPXOR Y9, Y8, Y8
VPSRLD $31, Y8, Y9
VPSLLD $1, Y8, Y4
VPOR Y9, Y4, Y4
VPSLLDQ $12, Y8, Y10 // 0, 0, 0, the input to W[i]
VPSRLD $30, Y10, Y9
VPSLLD $2, Y10, Y10
VPOR Y9, Y10, Y10 // rol1(W[i]) in the lane of W[i+3]
VPXOR Y10, Y4, Y4
VMOVDQU X4, 64(CX)
VEXTRACTI128 $1, Y4, 64(DX)
// W[20..23] = rol1(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]). W[i+3] needs
// W[i] from this group, which is added in afterwards.
VPALIGNR $8, Y1, Y2, Y8 // W[i-14..i-11]
VPXOR Y1, Y8, Y8 // W[i-16..i-13]
VPXOR Y3, Y8, Y8 // W[i-8..i-5]
VPSRLDQ $4, Y4, Y9 // W[i-3..i-1], 0
VPXOR Y9, Y8, Y8
VPSRLD $31, Y8, Y9
VPSLLD $1, Y8, Y5
VPOR Y9, Y5, Y5
VPSLLDQ $12, Y8, Y10 // 0, 0, 0, the input to W[i]
VPSRLD $30, Y10, Y9
VPSLLD $2, Y10, Y10
VPOR Y9, Y10, Y10 // rol1(W[i]) in the lane of W[i+3]
VPXOR Y10, Y5, Y5
VMOVDQU X5, 80(CX)
VEXTRACTI128 $1, Y5, 80(DX)
// W[24..27] = rol1(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]). W[i+3] needs
// W[i] from this group, which is added in afterwards.
VPALIGNR $8, Y2, Y3, Y8 // W[i-14..i-11]
VPXOR Y2, Y8, Y8 // W[i-16..i-13]
VPXOR Y4, Y8, Y8 // W[i-8..i-5]
VPSRLDQ $4, Y5, Y9 // W[i-3..i-1], 0
VPXOR Y9, Y8, Y8
VPSRLD $31, Y8, Y9
VPSLLD $1, Y8, Y6
VPOR Y9, Y6, Y6
VPSLLDQ $12, Y8, Y10 // 0, 0, 0, the input to W[i]
VPSRLD $30, Y10, Y9
VPSLLD $2, Y10, Y10
VPOR Y9, Y10, Y10 // rol1(W[i]) in the lane of W[i+3]
VPXOR Y10, Y6, Y6
VMOVDQU X6, 96(CX)
VEXTRACTI128 $1, Y6, 96(DX)
// W[28..31] = rol1(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]). W[i+3] needs
// W[i] from this group, which is added in afterwards.
VPALIGNR $8, Y3, Y4, Y8 // W[i-14..i-11]
VPXOR Y3, Y8, Y8 // W[i-16..i-13]
VPXOR Y5, Y8, Y8 // W[i-8..i-5]
VPSRLDQ $4, Y6, Y9 // W[i-3..i-1], 0
VPXOR Y9, Y8, Y8
VPSRLD $31, Y8, Y9
VPSLLD $1, Y8, Y7
VPOR Y9, Y7, Y7
VPSLLDQ $12, Y8, Y10 // 0, 0, 0, the input to W[i]
VPSRLD $30, Y10, Y9
VPSLLD $2, Y10, Y10
VPOR Y9, Y10, Y10 // rol1(W[i]) in the lane of W[i+3]
VPXOR Y10, Y7, Y7
VMOVDQU X7, 112(CX)
VEXTRACTI128 $1, Y7, 112(DX)
// W[32..35] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y6, Y7, Y8 // W[i-6..i-3]
VPXOR Y4, Y8, Y8 // W[i-16..i-13]
VPXOR Y1, Y8, Y8 // W[i-28..i-25]
VPXOR Y0, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y0
VPOR Y9, Y0, Y0
VMOVDQU X0, 128(CX)
VEXTRACTI128 $1, Y0, 128(DX)
// W[36..39] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y7, Y0, Y8 // W[i-6..i-3]
VPXOR Y5, Y8, Y8 // W[i-16..i-13]
VPXOR Y2, Y8, Y8 // W[i-28..i-25]
VPXOR Y1, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y1
VPOR Y9, Y1, Y1
VMOVDQU X1, 144(CX)
VEXTRACTI128 $1, Y1, 144(DX)
// W[40..43] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y0, Y1, Y8 // W[i-6..i-3]
VPXOR Y6, Y8, Y8 // W[i-16..i-13]
VPXOR Y3, Y8, Y8 // W[i-28..i-25]
VPXOR Y2, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y2
VPOR Y9, Y2, Y2
VMOVDQU X2, 160(CX)
VEXTRACTI128 $1, Y2, 160(DX)
// W[44..47] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y1, Y2, Y8 // W[i-6..i-3]
VPXOR Y7, Y8, Y8 // W[i-16..i-13]
VPXOR Y4, Y8, Y8 // W[i-28..i-25]
VPXOR Y3, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y3
VPOR Y9, Y3, Y3
VMOVDQU X3, 176(CX)
VEXTRACTI128 $1, Y3, 176(DX)
// W[48..51] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y2, Y3, Y8 // W[i-6..i-3]
VPXOR Y0, Y8, Y8 // W[i-16..i-13]
VPXOR Y5, Y8, Y8 // W[i-28..i-25]
VPXOR Y4, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y4
VPOR Y9, Y4, Y4
VMOVDQU X4, 192(CX)
VEXTRACTI128 $1, Y4, 192(DX)
// W[52..55] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y3, Y4, Y8 // W[i-6..i-3]
VPXOR Y1, Y8, Y8 // W[i-16..i-13]
VPXOR Y6, Y8, Y8 // W[i-28..i-25]
VPXOR Y5, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y5
VPOR Y9, Y5, Y5
VMOVDQU X5, 208(CX)
VEXTRACTI128 $1, Y5, 208(DX)
// W[56..59] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y4, Y5, Y8 // W[i-6..i-3]
VPXOR Y2, Y8, Y8 // W[i-16..i-13]
VPXOR Y7, Y8, Y8 // W[i-28..i-25]
VPXOR Y6, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y6
VPOR Y9, Y6, Y6
VMOVDQU X6, 224(CX)
VEXTRACTI128 $1, Y6, 224(DX)
// W[60..63] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y5, Y6, Y8 // W[i-6..i-3]
VPXOR Y3, Y8, Y8 // W[i-16..i-13]
VPXOR Y0, Y8, Y8 // W[i-28..i-25]
VPXOR Y7, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y7
VPOR Y9, Y7, Y7
VMOVDQU X7, 240(CX)
VEXTRACTI128 $1, Y7, 240(DX)
// W[64..67] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y6, Y7, Y8 // W[i-6..i-3]
VPXOR Y4, Y8, Y8 // W[i-16..i-13]
VPXOR Y1, Y8, Y8 // W[i-28..i-25]
VPXOR Y0, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y0
VPOR Y9, Y0, Y0
VMOVDQU X0, 256(CX)
VEXTRACTI128 $1, Y0, 256(DX)
// W[68..71] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y7, Y0, Y8 // W[i-6..i-3]
VPXOR Y5, Y8, Y8 // W[i-16..i-13]
VPXOR Y2, Y8, Y8 // W[i-28..i-25]
VPXOR Y1, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y1
VPOR Y9, Y1, Y1
VMOVDQU X1, 272(CX)
VEXTRACTI128 $1, Y1, 272(DX)
// W[72..75] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y0, Y1, Y8 // W[i-6..i-3]
VPXOR Y6, Y8, Y8 // W[i-16..i-13]
VPXOR Y3, Y8, Y8 // W[i-28..i-25]
VPXOR Y2, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y2
VPOR Y9, Y2, Y2
VMOVDQU X2, 288(CX)
VEXTRACTI128 $1, Y2, 288(DX)
// W[76..79] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y1, Y2, Y8 // W[i-6..i-3]
VPXOR Y7, Y8, Y8 // W[i-16..i-13]
VPXOR Y4, Y8, Y8 // W[i-28..i-25]
VPXOR Y3, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y3
VPOR Y9, Y3, Y3
VMOVDQU X3, 304(CX)
VEXTRACTI128 $1, Y3, 304(DX)
VZEROUPPER
RET
// Each round adds W[i], K, f(b, c, d) and rol5(a) into e and rotates b by 30.
// The callers rotate the register names instead of moving values around, and
// rol5(a) goes in last, as a is the only input the previous round produced.
#define ROUND_CH(a, b, c, d, e, i) \
ADDL ((i)*4)(SI), e; \
ADDL $0x5a827999, e; \
ANDNL d, b, AX; \
MOVL c, BX; \
ANDL b, BX; \
ADDL AX, e; \
ADDL BX, e; \
RORXL $27, a, CX; \
RORXL $2, b, b; \
ADDL CX, e
#define ROUND_PARITY(a, b, c, d, e, i, k) \
ADDL ((i)*4)(SI), e; \
ADDL k, e; \
MOVL b, AX; \
XORL c, AX; \
XORL d, AX; \
ADDL AX, e; \
RORXL $27, a, CX; \
RORXL $2, b, b; \
ADDL CX, e
// maj(b, c, d) = (b & c) + ((b ^ c) & d), as the two never share a bit.
#define ROUND_MAJ(a, b, c, d, e, i) \
ADDL ((i)*4)(SI), e; \
ADDL $0x8f1bbcdc, e; \
MOVL b, AX; \
XORL c, AX; \
ANDL d, AX; \
MOVL b, BX; \
ANDL c, BX; \
ADDL AX, e; \
ADDL BX, e; \
RORXL $27, a, CX; \
RORXL $2, b, b; \
ADDL CX, e
#define SAVECS(a, b, c, d, e, index) \
MOVL a, ((index)*20+0)(DX); \
MOVL b, ((index)*20+4)(DX); \
MOVL c, ((index)*20+8)(DX); \
MOVL d, ((index)*20+12)(DX); \
MOVL e, ((index)*20+16)(DX)
// func roundsBMI2(h *[5]uint32, m1 *[80]uint32, cs *[3][5]uint32)
// Requires: BMI1, BMI2
//
// Compresses the block whose schedule is in m1 into h, storing the states
// before steps 0, 58 and 65 into cs.
TEXT ·roundsBMI2(SB), NOSPLIT, $0-24
MOVQ h+0(FP), DI
MOVQ m1+8(FP), SI
MOVQ cs+16(FP), DX
MOVL 0(DI), R8
MOVL 4(DI), R9
MOVL 8(DI), R10
MOVL 12(DI), R11
MOVL 16(DI), R12
SAVECS(R8, R9, R10, R11, R12, 0)
ROUND_CH(R8, R9, R10, R11, R12, 0)
ROUND_CH(R12, R8, R9, R10, R11, 1)
ROUND_CH(R11, R12, R8, R9, R10, 2)
ROUND_CH(R10, R11, R12, R8, R9, 3)
ROUND_CH(R9, R10, R11, R12, R8, 4)
ROUND_CH(R8, R9, R10, R11, R12, 5)
ROUND_CH(R12, R8, R9, R10, R11, 6)
ROUND_CH(R11, R12, R8, R9, R10, 7)
ROUND_CH(R10, R11, R12, R8, R9, 8)
ROUND_CH(R9, R10, R11, R12, R8, 9)
ROUND_CH(R8, R9, R10, R11, R12, 10)
ROUND_CH(R12, R8, R9, R10, R11, 11)
ROUND_CH(R11, R12, R8, R9, R10, 12)
ROUND_CH(R10, R11, R12, R8, R9, 13)
ROUND_CH(R9, R10, R11, R12, R8, 14)
ROUND_CH(R8, R9, R10, R11, R12, 15)
ROUND_CH(R12, R8, R9, R10, R11, 16)
ROUND_CH(R11, R12, R8, R9, R10, 17)
ROUND_CH(R10, R11, R12, R8, R9, 18)
ROUND_CH(R9, R10, R11, R12, R8, 19)
ROUND_PARITY(R8, R9, R10, R11, R12, 20, $0x6ed9eba1)
ROUND_PARITY(R12, R8, R9, R10, R11, 21, $0x6ed9eba1)
ROUND_PARITY(R11, R12, R8, R9, R10, 22, $0x6ed9eba1)
ROUND_PARITY(R10, R11, R12, R8, R9, 23, $0x6ed9eba1)
ROUND_PARITY(R9, R10, R11, R12, R8, 24, $0x6ed9eba1)
ROUND_PARITY(R8, R9, R10, R11, R12, 25, $0x6ed9eba1)
ROUND_PARITY(R12, R8, R9, R10, R11, 26, $0x6ed9eba1)
ROUND_PARITY(R11, R12, R8, R9, R10, 27, $0x6ed9eba1)
ROUND_PARITY(R10, R11, R12, R8, R9, 28, $0x6ed9eba1)
ROUND_PARITY(R9, R10, R11, R12, R8, 29, $0x6ed9eba1)
ROUND_PARITY(R8, R9, R10, R11, R12, 30, $0x6ed9eba1)
ROUND_PARITY(R12, R8, R9, R10, R11, 31, $0x6ed9eba1)
ROUND_PARITY(R11, R12, R8, R9, R10, 32, $0x6ed9eba1)
ROUND_PARITY(R10, R11, R12, R8, R9, 33, $0x6ed9eba1)
ROUND_PARITY(R9, R10, R11, R12, R8, 34, $0x6ed9eba1)
ROUND_PARITY(R8, R9, R10, R11, R12, 35, $0x6ed9eba1)
ROUND_PARITY(R12, R8, R9, R10, R11, 36, $0x6ed9eba1)
ROUND_PARITY(R11, R12, R8, R9, R10, 37, $0x6ed9eba1)
ROUND_PARITY(R10, R11, R12, R8, R9, 38, $0x6ed9eba1)
ROUND_PARITY(R9, R10, R11, R12, R8, 39, $0x6ed9eba1)
ROUND_MAJ(R8, R9, R10, R11, R12, 40)
ROUND_MAJ(R12, R8, R9, R10, R11, 41)
ROUND_MAJ(R11, R12, R8, R9, R10, 42)
ROUND_MAJ(R10, R11, R12, R8, R9, 43)
ROUND_MAJ(R9, R10, R11, R12, R8, 44)
ROUND_MAJ(R8, R9, R10, R11, R12, 45)
ROUND_MAJ(R12, R8, R9, R10, R11, 46)
ROUND_MAJ(R11, R12, R8, R9, R10, 47)
ROUND_MAJ(R10, R11, R12, R8, R9, 48)
ROUND_MAJ(R9, R10, R11, R12, R8, 49)
ROUND_MAJ(R8, R9, R10, R11, R12, 50)
ROUND_MAJ(R12, R8, R9, R10, R11, 51)
ROUND_MAJ(R11, R12, R8, R9, R10, 52)
ROUND_MAJ(R10, R11, R12, R8, R9, 53)
ROUND_MAJ(R9, R10, R11, R12, R8, 54)
ROUND_MAJ(R8, R9, R10, R11, R12, 55)
ROUND_MAJ(R12, R8, R9, R10, R11, 56)
ROUND_MAJ(R11, R12, R8, R9, R10, 57)
SAVECS(R10, R11, R12, R8, R9, 1)
ROUND_MAJ(R10, R11, R12, R8, R9, 58)
ROUND_MAJ(R9, R10, R11, R12, R8, 59)
ROUND_PARITY(R8, R9, R10, R11, R12, 60, $0xca62c1d6)
ROUND_PARITY(R12, R8, R9, R10, R11, 61, $0xca62c1d6)
ROUND_PARITY(R11, R12, R8, R9, R10, 62, $0xca62c1d6)
ROUND_PARITY(R10, R11, R12, R8, R9, 63, $0xca62c1d6)
ROUND_PARITY(R9, R10, R11, R12, R8, 64, $0xca62c1d6)
SAVECS(R8, R9, R10, R11, R12, 2)
ROUND_PARITY(R8, R9, R10, R11, R12, 65, $0xca62c1d6)
ROUND_PARITY(R12, R8, R9, R10, R11, 66, $0xca62c1d6)
ROUND_PARITY(R11, R12, R8, R9, R10, 67, $0xca62c1d6)
ROUND_PARITY(R10, R11, R12, R8, R9, 68, $0xca62c1d6)
ROUND_PARITY(R9, R10, R11, R12, R8, 69, $0xca62c1d6)
ROUND_PARITY(R8, R9, R10, R11, R12, 70, $0xca62c1d6)
ROUND_PARITY(R12, R8, R9, R10, R11, 71, $0xca62c1d6)
ROUND_PARITY(R11, R12, R8, R9, R10, 72, $0xca62c1d6)
ROUND_PARITY(R10, R11, R12, R8, R9, 73, $0xca62c1d6)
ROUND_PARITY(R9, R10, R11, R12, R8, 74, $0xca62c1d6)
ROUND_PARITY(R8, R9, R10, R11, R12, 75, $0xca62c1d6)
ROUND_PARITY(R12, R8, R9, R10, R11, 76, $0xca62c1d6)
ROUND_PARITY(R11, R12, R8, R9, R10, 77, $0xca62c1d6)
ROUND_PARITY(R10, R11, R12, R8, R9, 78, $0xca62c1d6)
ROUND_PARITY(R9, R10, R11, R12, R8, 79, $0xca62c1d6)
ADDL R8, 0(DI)
ADDL R9, 4(DI)
ADDL R10, 8(DI)
ADDL R11, 12(DI)
ADDL R12, 16(DI)
RET
// Swaps the bytes of each word, as SHA-1 reads the block big endian.
DATA bswap_mask<>+0(SB)/8, $0x0405060700010203
DATA bswap_mask<>+8(SB)/8, $0x0c0d0e0f08090a0b
GLOBL bswap_mask<>(SB), RODATA|NOPTR, $16
+32 -46
View File
@@ -20,7 +20,9 @@ var forceGeneric bool
// blockGeneric is a portable, pure Go version of the SHA-1 block step.
// It's used by sha1block_generic.go and tests.
func blockGeneric(dig *digest, p []byte) {
var w [16]uint32
// Expand directly into the schedule retained for collision detection.
// Every word is overwritten for each block, including rehashes.
var m1 [shared.Rounds]uint32
// cs stores the pre-step compression state for only the steps required for the
// collision detection, which are 0, 58 and 65.
@@ -29,7 +31,6 @@ func blockGeneric(dig *digest, p []byte) {
h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4]
for len(p) >= shared.Chunk {
m1 := [shared.Rounds]uint32{}
hi := 1
// Collision attacks are thwarted by hashing a detected near-collision block 3 times.
@@ -51,36 +52,27 @@ func blockGeneric(dig *digest, p []byte) {
for ; i < 16; i++ {
// load step
j := i * 4
w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
m1[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
f := b&c | (^b)&d
t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K0
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K0
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
// Store compression state for the collision detection.
m1[i] = w[i&0xf]
}
for ; i < 20; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
tmp := m1[i-3] ^ m1[i-8] ^ m1[i-14] ^ m1[i-16]
m1[i] = tmp<<1 | tmp>>(32-1)
f := b&c | (^b)&d
t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K0
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K0
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
// Store compression state for the collision detection.
m1[i] = w[i&0xf]
}
for ; i < 40; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
tmp := m1[i-3] ^ m1[i-8] ^ m1[i-14] ^ m1[i-16]
m1[i] = tmp<<1 | tmp>>(32-1)
f := b ^ c ^ d
t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K1
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K1
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
// Store compression state for the collision detection.
m1[i] = w[i&0xf]
}
for ; i < 60; i++ {
if i == 58 {
@@ -88,15 +80,12 @@ func blockGeneric(dig *digest, p []byte) {
cs[1] = [shared.WordBuffers]uint32{a, b, c, d, e}
}
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
tmp := m1[i-3] ^ m1[i-8] ^ m1[i-14] ^ m1[i-16]
m1[i] = tmp<<1 | tmp>>(32-1)
f := ((b | c) & d) | (b & c)
t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K2
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K2
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
// Store compression state for the collision detection.
m1[i] = w[i&0xf]
}
for ; i < 80; i++ {
if i == 65 {
@@ -104,15 +93,12 @@ func blockGeneric(dig *digest, p []byte) {
cs[2] = [shared.WordBuffers]uint32{a, b, c, d, e}
}
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
tmp := m1[i-3] ^ m1[i-8] ^ m1[i-14] ^ m1[i-16]
m1[i] = tmp<<1 | tmp>>(32-1)
f := b ^ c ^ d
t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K3
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K3
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
// Store compression state for the collision detection.
m1[i] = w[i&0xf]
}
h0 += a
@@ -128,7 +114,7 @@ func blockGeneric(dig *digest, p []byte) {
if hi == 1 {
h := [shared.WordBuffers]uint32{h0, h1, h2, h3, h4}
col := checkCollision(&m1, &cs, &h)
col := checkCollision(&m1, &cs, &h, ubc.CalculateDvMask(&m1))
if col {
dig.col = true
hi++
@@ -147,8 +133,9 @@ func checkCollision(
m1 *[shared.Rounds]uint32,
cs *[shared.PreStepState][shared.WordBuffers]uint32,
h *[shared.WordBuffers]uint32,
mask uint32,
) bool {
if mask := ubc.CalculateDvMask(m1); mask != 0 {
if mask != 0 {
dvs := ubc.SHA1_dvs()
for i := 0; dvs[i].DvType != 0; i++ {
@@ -290,23 +277,22 @@ func rectifyCompressionState(
return
}
func3 := func(state [shared.WordBuffers]uint32, i int) [shared.WordBuffers]uint32 {
a, b, c, d, e := state[0], state[1], state[2], state[3], state[4]
// The words are loaded and stored one at a time. Passing [5]uint32 values
// around made the compiler reload them with wider moves than they were
// stored with, which stalled on store forwarding each time.
// Advance cs[1] from the state before step 56 to the one before step 58.
a, b, c, d, e := cs[1][0], cs[1][1], cs[1][2], cs[1][3], cs[1][4]
for i := 56; i < 58; i++ {
f := ((b | c) & d) | (b & c)
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K2
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
return [shared.WordBuffers]uint32{a, b, c, d, e}
}
func4 := func(state [shared.WordBuffers]uint32, i int) [shared.WordBuffers]uint32 {
a, b, c, d, e := state[0], state[1], state[2], state[3], state[4]
f := b ^ c ^ d
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K3
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
return [shared.WordBuffers]uint32{a, b, c, d, e}
}
cs[1][0], cs[1][1], cs[1][2], cs[1][3], cs[1][4] = a, b, c, d, e
cs57 := func3(cs[1], 56)
cs[1] = func3(cs57, 57)
cs[2] = func4(cs[2], 64)
// Advance cs[2] from the state before step 64 to the one before step 65.
a, b, c, d, e = cs[2][0], cs[2][1], cs[2][2], cs[2][3], cs[2][4]
f := b ^ c ^ d
t := bits.RotateLeft32(a, 5) + f + e + m1[64] + shared.K3
cs[2][0], cs[2][1], cs[2][2], cs[2][3], cs[2][4] = t, a, bits.RotateLeft32(b, 30), c, d
}
+51
View File
@@ -0,0 +1,51 @@
//go:build !noasm && gc && amd64
package ubc
import (
"unsafe"
"github.com/pjbgf/sha1cd/internal/cpu"
)
// useAVX512 reports whether calculateDvMaskAVX512 can run. Its only
// instructions outside AVX-512F are VMOVD and VZEROUPPER, which need AVX.
// HasAVX512F is set only when the CPU reports AVX as well.
var useAVX512 = cpu.X86.HasAVX512F
//go:noescape
func calculateDvMaskAVX512(W *[80]uint32, terms *uint32, groups int) uint32
// avx512Lanes is the number of terms a group of avx512Terms describes, and
// avx512Fields the vectors it holds for them.
const (
avx512Lanes = 16
avx512Fields = 6
)
// avx512Groups is how many passes of the kernel loop the table needs.
const avx512Groups = len(avx512Terms) / (avx512Fields * avx512Lanes)
// alignedTerms is a copy of avx512Terms that starts on a cache line. The
// linker aligns data to 32 bytes at most, and a 64 byte load that straddles
// two cache lines costs about twice as much.
var alignedTerms = func() *uint32 {
buf := make([]uint32, len(avx512Terms)+16)
off := (64 - uintptr(unsafe.Pointer(&buf[0]))%64) % 64 / 4
copy(buf[off:], avx512Terms[:])
return &buf[off]
}()
// CalculateDvMask takes as input an expanded message block and
// verifies the unavoidable bitconditions for all listed DVs. It returns
// a dvmask where each bit belonging to a DV is set if all unavoidable
// bitconditions for that DV have been met.
func CalculateDvMask(W *[80]uint32) uint32 {
if W == nil {
return 0
}
if useAVX512 {
return calculateDvMaskAVX512(W, alignedTerms, avx512Groups)
}
return calculateDvMaskGeneric(W)
}
+52
View File
@@ -0,0 +1,52 @@
//go:build !noasm && gc && amd64
#include "textflag.h"
// func calculateDvMaskAVX512(W *[80]uint32, terms *uint32, groups int) uint32
// Requires: AVX, AVX512F
//
// Each group of avx512Terms describes 16 of the bit tests of CalculateDvMask,
// as vectors of word indices, shift counts, expected parities and the masks
// that clear the DVs of a failing test. One pass of the loop runs a group.
//
// terms only has to be 64 byte aligned for speed, as otherwise every load of
// it straddles two cache lines. The word indices are biased by 34, matching
// the two vectors of W loaded below.
TEXT ·calculateDvMaskAVX512(SB), NOSPLIT, $0-28
MOVQ W+0(FP), AX
MOVQ terms+8(FP), BX
MOVQ groups+16(FP), CX
VMOVDQU32 136(AX), Z0 // W[34..49]
VMOVDQU32 200(AX), Z1 // W[50..65]
VPTERNLOGD $0xff, Z2, Z2, Z2 // mask = all ones
MOVL $1, DX
VPBROADCASTD DX, Z3
loop:
VMOVDQU32 0(BX), Z4
VMOVDQU32 64(BX), Z5
VPERMI2D Z1, Z0, Z4 // W[a]
VPERMI2D Z1, Z0, Z5 // W[b]
VPSRLVD 128(BX), Z4, Z4 // >> ka
VPSRLVD 192(BX), Z5, Z5 // >> kb
VPTERNLOGD $0x96, 256(BX), Z5, Z4 // ^ e
VPTESTMD Z3, Z4, K1 // the terms whose bit test fails
VPANDD 320(BX), Z2, K1, Z2 // clear their DVs
ADDQ $384, BX
DECQ CX
JNZ loop
// AND the 16 lanes together, first the 128 bit lanes and then the
// words within them.
VSHUFI64X2 $0x4e, Z2, Z2, Z4
VPANDD Z4, Z2, Z2
VSHUFI64X2 $0xb1, Z2, Z2, Z4
VPANDD Z4, Z2, Z2
VPSHUFD $0x4e, Z2, Z4
VPANDD Z4, Z2, Z2
VPSHUFD $0xb1, Z2, Z4
VPANDD Z4, Z2, Z2
VMOVD X2, AX
VZEROUPPER
MOVL AX, ret+24(FP)
RET
+111
View File
@@ -0,0 +1,111 @@
//go:build !noasm && gc && amd64
package ubc
// avx512Terms holds the bit tests of CalculateDvMask in the layout the
// AVX-512 kernel consumes, 16 terms per group. Each term says that a set of
// DVs survives only if bit ka of W[a] XOR bit kb of W[b] equals e, and names
// the mask that clears those DVs when it does not.
//
// The six vectors of a group are, in order: a-34, b-34, ka, kb, e, and the
// clear mask. Word indices are biased by 34 because the kernel keeps W[34..65]
// in two registers. A group is 384 bytes, and the kernel runs one per pass.
//
// The terms are those of calculateDvMaskGeneric, which is a port of
// ubc_check.c and has not changed since the collision detection was published
// in 2017. TestAVX512MatchesGeneric holds the two implementations to the same
// results over inputs that reach every DV, so a mistake here is a test
// failure rather than a missed collision. The comment above each group names
// its terms as a.ka^b.kb=e.
var avx512Terms = [960]uint32{
// terms 0-15
// 44.29^45.29=0 49.29^50.29=0 48.29^49.29=0 47.4^50.29=0 47.29^48.29=0 46.4^49.29=0 46.29^47.29=0 45.4^48.29=0
// 45.29^46.29=0 44.4^47.29=0 43.4^46.29=0 43.29^44.29=0 42.4^45.29=0 41.4^44.29=0 40.29^41.29=0 54.29^55.29=0
0x0000000a, 0x0000000f, 0x0000000e, 0x0000000d, 0x0000000d, 0x0000000c, 0x0000000c, 0x0000000b, 0x0000000b, 0x0000000a, 0x00000009, 0x00000009, 0x00000008, 0x00000007, 0x00000006, 0x00000014,
0x0000000b, 0x00000010, 0x0000000f, 0x00000010, 0x0000000e, 0x0000000f, 0x0000000d, 0x0000000e, 0x0000000c, 0x0000000d, 0x0000000c, 0x0000000a, 0x0000000b, 0x0000000a, 0x00000007, 0x00000015,
0x0000001d, 0x0000001d, 0x0000001d, 0x00000004, 0x0000001d, 0x00000004, 0x0000001d, 0x00000004, 0x0000001d, 0x00000004, 0x00000004, 0x0000001d, 0x00000004, 0x00000004, 0x0000001d, 0x0000001d,
0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xfd7c5f7f, 0x3d7efff7, 0x9f5f7ffb, 0x7dfedddf, 0xcfcfdffd, 0xbf7f7777, 0xe7e7f7fe, 0xdfdfdddb, 0xf5f57dff, 0xefeff775, 0xf7f7fdda, 0xff5ed7df, 0xfdfd7f75, 0xff7edfda, 0x7ff5ff5d, 0x3f77dfff,
// terms 16-31
// 53.29^54.29=0 52.29^53.29=0 50.4^53.29=0 50.29^51.29=0 49.4^52.29=0 48.4^51.29=0 42.29^43.29=0 41.29^42.29=0
// 40.4^43.29=0 39.4^42.29=0 38.4^41.29=0 37.4^40.29=0 55.29^56.29=0 52.4^55.29=0 51.4^54.29=0 51.29^52.29=0
0x00000013, 0x00000012, 0x00000010, 0x00000010, 0x0000000f, 0x0000000e, 0x00000008, 0x00000007, 0x00000006, 0x00000005, 0x00000004, 0x00000003, 0x00000015, 0x00000012, 0x00000011, 0x00000011,
0x00000014, 0x00000013, 0x00000013, 0x00000011, 0x00000012, 0x00000011, 0x00000009, 0x00000008, 0x00000009, 0x00000008, 0x00000007, 0x00000006, 0x00000016, 0x00000015, 0x00000014, 0x00000012,
0x0000001d, 0x0000001d, 0x00000004, 0x0000001d, 0x00000004, 0x00000004, 0x0000001d, 0x0000001d, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x0000001d, 0x00000004, 0x00000004, 0x0000001d,
0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x9fddf7ff, 0xcfeefdff, 0xdfed77ff, 0x75fdffdf, 0xeff6ddff, 0xf7fd777f, 0xffcff5f7, 0xffe7fd7b, 0x7fdff7f5, 0xbfeffdfa, 0x5ff7ff7d, 0xaffdffde, 0x7def7fff, 0x7f6f7fff, 0xbfd7dfff, 0xe7f7ff7f,
// terms 32-47
// 36.4^40.29=0 53.29^56.29=1 51.29^54.29=1 50.29^52.29=1 49.29^51.29=1 48.29^50.29=1 47.29^49.29=1 46.29^48.29=1
// 45.6^47.6=0 45.29^47.29=1 44.6^46.6=0 44.29^46.29=1 41.1^42.6=1 40.1^41.6=1 40.4^42.4=1 39.1^40.6=1
0x00000002, 0x00000013, 0x00000011, 0x00000010, 0x0000000f, 0x0000000e, 0x0000000d, 0x0000000c, 0x0000000b, 0x0000000b, 0x0000000a, 0x0000000a, 0x00000007, 0x00000006, 0x00000006, 0x00000005,
0x00000006, 0x00000016, 0x00000014, 0x00000012, 0x00000011, 0x00000010, 0x0000000f, 0x0000000e, 0x0000000d, 0x0000000d, 0x0000000c, 0x0000000c, 0x00000008, 0x00000007, 0x00000008, 0x00000006,
0x00000004, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x00000006, 0x0000001d, 0x00000006, 0x0000001d, 0x00000001, 0x00000001, 0x00000004, 0x00000001,
0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x00000006, 0x0000001d, 0x00000006, 0x0000001d, 0x00000006, 0x00000006, 0x00000004, 0x00000006,
0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
0xffeefdf7, 0xffcf7fff, 0xfff5f7ff, 0xfffeddff, 0xffff777f, 0xffffdddf, 0xfffff777, 0xfffffddb, 0xffffbbbf, 0xffffff75, 0xffffeeef, 0xffffffda, 0xfbfbfeff, 0xfeffbfbf, 0x7ffffff5, 0xffbfefef,
// terms 48-63
// 39.4^41.4=1 38.4^40.4=1 37.4^39.4=1 36.1^37.6=1 35.4^39.29=0 63.0^64.5=1 63.1^64.6=1 62.0^63.5=1
// 61.0^62.5=1 61.2^62.7=1 60.0^61.5=1 58.29^59.29=0 57.29^58.29=0 56.4^59.29=0 56.29^59.29=1 56.29^57.29=0
0x00000005, 0x00000004, 0x00000003, 0x00000002, 0x00000001, 0x0000001d, 0x0000001d, 0x0000001c, 0x0000001b, 0x0000001b, 0x0000001a, 0x00000018, 0x00000017, 0x00000016, 0x00000016, 0x00000016,
0x00000007, 0x00000006, 0x00000005, 0x00000003, 0x00000005, 0x0000001e, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001c, 0x0000001b, 0x00000019, 0x00000018, 0x00000019, 0x00000019, 0x00000017,
0x00000004, 0x00000004, 0x00000004, 0x00000001, 0x00000004, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x0000001d, 0x0000001d, 0x00000004, 0x0000001d, 0x0000001d,
0x00000004, 0x00000004, 0x00000004, 0x00000006, 0x0000001d, 0x00000005, 0x00000006, 0x00000005, 0x00000005, 0x00000007, 0x00000005, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d,
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
0xbffffffa, 0x5ffffffd, 0xaffffffe, 0xfffbefbf, 0xfff7ff7b, 0xffefff7f, 0xfffefffb, 0xfff7ffdf, 0xfffdfff7, 0xfffbffef, 0xfffefffb, 0xddffffff, 0xef7fffff, 0xd7ffffff, 0xf5ffffff, 0xf7dfffff,
// terms 64-79
// 55.4^58.29=0 54.4^57.29=0 53.4^56.29=0 50.6^51.1=0 48.6^50.6=0 48.29^55.29=1 47.6^49.6=0 47.6^48.1=0
// 46.6^48.6=0 46.6^47.1=0 44.1^45.6=1 43.6^45.6=0 42.6^44.6=0 42.6^43.1=0 41.6^42.1=0 40.6^41.1=0
0x00000015, 0x00000014, 0x00000013, 0x00000010, 0x0000000e, 0x0000000e, 0x0000000d, 0x0000000d, 0x0000000c, 0x0000000c, 0x0000000a, 0x00000009, 0x00000008, 0x00000008, 0x00000007, 0x00000006,
0x00000018, 0x00000017, 0x00000016, 0x00000011, 0x00000010, 0x00000015, 0x0000000f, 0x0000000e, 0x0000000e, 0x0000000d, 0x0000000b, 0x0000000b, 0x0000000a, 0x00000009, 0x00000008, 0x00000007,
0x00000004, 0x00000004, 0x00000004, 0x00000006, 0x00000006, 0x0000001d, 0x00000006, 0x00000006, 0x00000006, 0x00000006, 0x00000001, 0x00000006, 0x00000006, 0x00000006, 0x00000006, 0x00000006,
0x0000001d, 0x0000001d, 0x0000001d, 0x00000001, 0x00000006, 0x0000001d, 0x00000006, 0x00000001, 0x00000006, 0x00000001, 0x00000006, 0x00000006, 0x00000006, 0x00000001, 0x00000001, 0x00000001,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xedffffff, 0xf77fffff, 0xfddfffff, 0xfffbefff, 0xfffbefff, 0xffff5fff, 0xffffbbff, 0xfbffffbf, 0xffffeeff, 0xfeffffef, 0xffbfbfff, 0xfffffbbf, 0xfffffeef, 0xfbfbffff, 0xfeffbfff, 0xffbfefff,
// terms 80-95
// 39.4^43.29=0 38.4^42.29=0 37.1^38.6=1 37.4^41.29=0 36.4^38.4=1 35.1^36.6=1 35.3^39.28=0 61.1^62.6=1
// 59.5^63.30=0 58.0^63.30=1 62.1^63.6=1 60.5^64.30=0 59.0^64.30=1 40.6^42.6=0 62.2^63.7=1 41.6^43.6=0
0x00000005, 0x00000004, 0x00000003, 0x00000003, 0x00000002, 0x00000001, 0x00000001, 0x0000001b, 0x00000019, 0x00000018, 0x0000001c, 0x0000001a, 0x00000019, 0x00000006, 0x0000001c, 0x00000007,
0x00000009, 0x00000008, 0x00000004, 0x00000007, 0x00000004, 0x00000002, 0x00000005, 0x0000001c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001e, 0x0000001e, 0x00000008, 0x0000001d, 0x00000009,
0x00000004, 0x00000004, 0x00000001, 0x00000004, 0x00000004, 0x00000001, 0x00000003, 0x00000001, 0x00000005, 0x00000000, 0x00000001, 0x00000005, 0x00000000, 0x00000006, 0x00000002, 0x00000006,
0x0000001d, 0x0000001d, 0x00000006, 0x0000001d, 0x00000004, 0x00000006, 0x0000001c, 0x00000006, 0x0000001e, 0x0000001e, 0x00000006, 0x0000001e, 0x0000001e, 0x00000006, 0x00000007, 0x00000006,
0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000,
0xfdff7fff, 0xff7fdfff, 0xffffbeff, 0xffdff7ff, 0xd7ffffff, 0xfffffbef, 0xfff7dfff, 0xfffffffe, 0xfffffffe, 0xfffffffe, 0xfffffffd, 0xfffffffd, 0xfffffffd, 0xffffffef, 0xffffffbf, 0xffffffbf,
// terms 96-111
// 63.2^64.7=1 48.6^49.1=0 49.6^50.1=0 42.1^50.1=1 39.6^40.1=0 38.1^40.1=1 36.4^37.4=1 43.1^51.1=1
// 37.4^38.4=1 51.6^52.1=0 49.6^51.6=0 37.1^37.6=0 35.5^39.30=0 38.4^39.4=1 47.1^51.1=1 36.3^40.28=0
0x0000001d, 0x0000000e, 0x0000000f, 0x00000008, 0x00000005, 0x00000004, 0x00000002, 0x00000009, 0x00000003, 0x00000011, 0x0000000f, 0x00000003, 0x00000001, 0x00000004, 0x0000000d, 0x00000002,
0x0000001e, 0x0000000f, 0x00000010, 0x00000010, 0x00000006, 0x00000006, 0x00000003, 0x00000011, 0x00000004, 0x00000012, 0x00000011, 0x00000003, 0x00000005, 0x00000005, 0x00000011, 0x00000006,
0x00000002, 0x00000006, 0x00000006, 0x00000001, 0x00000006, 0x00000001, 0x00000004, 0x00000001, 0x00000004, 0x00000006, 0x00000006, 0x00000001, 0x00000005, 0x00000004, 0x00000001, 0x00000003,
0x00000007, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000004, 0x00000001, 0x00000004, 0x00000001, 0x00000006, 0x00000006, 0x0000001e, 0x00000004, 0x00000001, 0x0000001c,
0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
0xfffffeff, 0xfffffeff, 0xfffffbff, 0xfffffbff, 0xfffffbff, 0xfffffbff, 0xfffff7ff, 0xffffefff, 0xffffdfff, 0xffffbfff, 0xffffbfff, 0xffffbfff, 0xffffbfff, 0xffff7fff, 0xfffbffff, 0xffefffff,
// terms 112-127
// 35.30^40.28=1 37.3^41.28=0 36.30^41.28=1 53.6^54.1=0 51.6^53.6=0 50.1^54.1=1 45.6^46.1=0 37.5^41.30=0
// 36.0^41.30=1 55.29^58.29=1 38.3^42.28=0 37.30^42.28=1 54.6^55.1=0 52.6^54.6=0 51.1^55.1=1 45.1^47.1=1
0x00000001, 0x00000003, 0x00000002, 0x00000013, 0x00000011, 0x00000010, 0x0000000b, 0x00000003, 0x00000002, 0x00000015, 0x00000004, 0x00000003, 0x00000014, 0x00000012, 0x00000011, 0x0000000b,
0x00000006, 0x00000007, 0x00000007, 0x00000014, 0x00000013, 0x00000014, 0x0000000c, 0x00000007, 0x00000007, 0x00000018, 0x00000008, 0x00000008, 0x00000015, 0x00000014, 0x00000015, 0x0000000d,
0x0000001e, 0x00000003, 0x0000001e, 0x00000006, 0x00000006, 0x00000001, 0x00000006, 0x00000005, 0x00000000, 0x0000001d, 0x00000003, 0x0000001e, 0x00000006, 0x00000006, 0x00000001, 0x00000001,
0x0000001c, 0x0000001c, 0x0000001c, 0x00000001, 0x00000006, 0x00000001, 0x00000001, 0x0000001e, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001c, 0x00000001, 0x00000006, 0x00000001, 0x00000001,
0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001,
0xffefffff, 0xffdfffff, 0xffdfffff, 0xffbfffff, 0xffbfffff, 0xffbfffff, 0xffbfffff, 0xffbfffff, 0xffbfffff, 0xff7fffff, 0xff7fffff, 0xff7fffff, 0xfeffffff, 0xfeffffff, 0xfeffffff, 0xfeffffff,
// terms 128-143
// 38.5^42.30=0 37.0^42.30=1 39.3^43.28=0 38.30^43.28=1 55.6^56.1=0 53.6^55.6=0 52.1^56.1=1 46.1^48.1=1
// 39.5^43.30=0 38.0^43.30=1 59.29^60.29=0 40.3^44.28=0 40.4^44.29=0 39.30^44.28=1 58.29^61.29=1 57.4^61.29=0
0x00000004, 0x00000003, 0x00000005, 0x00000004, 0x00000015, 0x00000013, 0x00000012, 0x0000000c, 0x00000005, 0x00000004, 0x00000019, 0x00000006, 0x00000006, 0x00000005, 0x00000018, 0x00000017,
0x00000008, 0x00000008, 0x00000009, 0x00000009, 0x00000016, 0x00000015, 0x00000016, 0x0000000e, 0x00000009, 0x00000009, 0x0000001a, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000001b, 0x0000001b,
0x00000005, 0x00000000, 0x00000003, 0x0000001e, 0x00000006, 0x00000006, 0x00000001, 0x00000001, 0x00000005, 0x00000000, 0x0000001d, 0x00000003, 0x00000004, 0x0000001e, 0x0000001d, 0x00000004,
0x0000001e, 0x0000001e, 0x0000001c, 0x0000001c, 0x00000001, 0x00000006, 0x00000001, 0x00000001, 0x0000001e, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001d, 0x0000001c, 0x0000001d, 0x0000001d,
0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
0xfeffffff, 0xfeffffff, 0xfdffffff, 0xfdffffff, 0xfbffffff, 0xfbffffff, 0xfbffffff, 0xfbffffff, 0xfbffffff, 0xfbffffff, 0xf7ffffff, 0xf7ffffff, 0xf7ffffff, 0xf7ffffff, 0xefffffff, 0xefffffff,
// terms 144-159
// 41.3^45.28=0 41.4^45.29=0 58.4^62.29=0 42.3^46.28=0 42.4^46.29=0 59.4^63.29=0 57.4^59.29=0 43.3^47.28=0
// 43.4^47.29=0 60.4^64.29=0 44.3^48.28=0 44.4^48.29=0 35.0^35.0=0 35.0^35.0=0 35.0^35.0=0 35.0^35.0=0
0x00000007, 0x00000007, 0x00000018, 0x00000008, 0x00000008, 0x00000019, 0x00000017, 0x00000009, 0x00000009, 0x0000001a, 0x0000000a, 0x0000000a, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
0x0000000b, 0x0000000b, 0x0000001c, 0x0000000c, 0x0000000c, 0x0000001d, 0x00000019, 0x0000000d, 0x0000000d, 0x0000001e, 0x0000000e, 0x0000000e, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
0x00000003, 0x00000004, 0x00000004, 0x00000003, 0x00000004, 0x00000004, 0x00000004, 0x00000003, 0x00000004, 0x00000004, 0x00000003, 0x00000004, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x0000001c, 0x0000001d, 0x0000001d, 0x0000001c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001c, 0x0000001d, 0x0000001d, 0x0000001c, 0x0000001d, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xefffffff, 0xefffffff, 0xdfffffff, 0xdfffffff, 0xdfffffff, 0xbfffffff, 0xbfffffff, 0xbfffffff, 0xbfffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
}
+14
View File
@@ -0,0 +1,14 @@
//go:build noasm || !gc || !amd64
package ubc
// CalculateDvMask takes as input an expanded message block and
// verifies the unavoidable bitconditions for all listed DVs. It returns
// a dvmask where each bit belonging to a DV is set if all unavoidable
// bitconditions for that DV have been met.
func CalculateDvMask(W *[80]uint32) uint32 {
if W == nil {
return 0
}
return calculateDvMaskGeneric(W)
}
+4 -8
View File
@@ -23,16 +23,12 @@ type DvInfo struct {
Dm [80]uint32
}
// CalculateDvMask takes as input an expanded message block and
// verifies the unavoidable bitconditions for all listed DVs. It returns
// a dvmask where each bit belonging to a DV is set if all unavoidable
// bitconditions for that DV have been met.
// calculateDvMaskGeneric is the portable implementation of CalculateDvMask.
// The AVX-512 kernel runs the same bit tests from avx512Terms, and
// TestAVX512MatchesGeneric holds the two to the same results.
//
//go:nosplit
func CalculateDvMask(W *[80]uint32) uint32 {
if W == nil {
return 0
}
func calculateDvMaskGeneric(W *[80]uint32) uint32 {
mask := uint32(0xFFFFFFFF)
mask &= (((((W[44] ^ W[45]) >> 29) & 1) - 1) | ^(DV_I_48_0_bit | DV_I_51_0_bit | DV_I_52_0_bit | DV_II_45_0_bit | DV_II_46_0_bit | DV_II_50_0_bit | DV_II_51_0_bit))
mask &= (((((W[49] ^ W[50]) >> 29) & 1) - 1) | ^(DV_I_46_0_bit | DV_II_45_0_bit | DV_II_50_0_bit | DV_II_51_0_bit | DV_II_55_0_bit | DV_II_56_0_bit))
+15
View File
@@ -0,0 +1,15 @@
*
!.git/
!build/root.tgz
!cmd/
!config/
!internal/
!mod/
!pkg/
!regclient/
!scheme/
!types/
!vendor/
!go.*
!*.go
!Makefile
+5
View File
@@ -0,0 +1,5 @@
artifacts/
bin/
output/
vendor/
.regctl_conf_ci.json
+19
View File
@@ -0,0 +1,19 @@
# all lists use a `-`
MD004:
style: dash
# allow tabs in code blocks (for Go)
MD010:
code_blocks: false
# disable line length, prefer one sentence per line for PRs
MD013: false
# emphasis with underscore (`_emphasis_`)
MD049:
style: "underscore"
# bold with asterisk (`**bold**`)
MD050:
style: "asterisk"
+1
View File
@@ -0,0 +1 @@
GoVersionOverride = "1.26.3"
+53
View File
@@ -0,0 +1,53 @@
{"name":"docker-arg-alpine-digest","key":"docker.io/library/alpine:3.23.4","version":"sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11"}
{"name":"docker-arg-alpine-tag","key":"docker.io/library/alpine","version":"3.23.4"}
{"name":"docker-arg-ecr","key":"https://github.com/awslabs/amazon-ecr-credential-helper.git","version":"v0.12.0"}
{"name":"docker-arg-gcr","key":"https://github.com/GoogleCloudPlatform/docker-credential-gcr.git","version":"v2.1.32"}
{"name":"docker-arg-go-digest","key":"docker.io/library/golang:1.26.3-alpine","version":"sha256:91eda9776261207ea25fd06b5b7fed8d397dd2c0a283e77f2ab6e91bfa71079d"}
{"name":"docker-arg-go-tag","key":"docker.io/library/golang","version":"1.26.3"}
{"name":"docker-arg-lunajson","key":"https://github.com/grafi-tt/lunajson.git:master","version":"e3a9666eb1275741e887e29926b144f8daee3bef"}
{"name":"docker-arg-semver","key":"https://github.com/kikito/semver.lua.git:master","version":"a4b708ba243208d46e575da870af969dca46a94d"}
{"name":"gha-alpine-digest","key":"docker.io/library/alpine:3.23.4","version":"sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11"}
{"name":"gha-alpine-tag-base","key":"docker.io/library/alpine","version":"3"}
{"name":"gha-alpine-tag-comment","key":"docker.io/library/alpine","version":"3.23.4"}
{"name":"gha-cosign-version","key":"https://github.com/sigstore/cosign.git","version":"v3.0.6"}
{"name":"gha-golang-matrix","key":"golang-matrix","version":"[\"1.25\", \"1.26\"]"}
{"name":"gha-golang-release","key":"golang-latest","version":"1.26"}
{"name":"gha-syft-version","key":"docker.io/anchore/syft","version":"v1.44.0"}
{"name":"gha-uses-commit","key":"https://github.com/actions/checkout.git:v6.0.2","version":"de0fac2e4500dabe0009e67214ff5f5447ce83dd"}
{"name":"gha-uses-commit","key":"https://github.com/actions/setup-go.git:v6.4.0","version":"4a3601121dd01d1626a1e23e37211e3254c1c06c"}
{"name":"gha-uses-commit","key":"https://github.com/actions/stale.git:v10.3.0","version":"eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899"}
{"name":"gha-uses-commit","key":"https://github.com/actions/upload-artifact.git:v7.0.1","version":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"}
{"name":"gha-uses-commit","key":"https://github.com/anchore/sbom-action.git:v0.24.0","version":"e22c389904149dbc22b58101806040fa8d37a610"}
{"name":"gha-uses-commit","key":"https://github.com/docker/build-push-action.git:v7.2.0","version":"f9f3042f7e2789586610d6e8b85c8f03e5195baf"}
{"name":"gha-uses-commit","key":"https://github.com/docker/login-action.git:v4.2.0","version":"650006c6eb7dba73a995cc03b0b2d7f5ca915bee"}
{"name":"gha-uses-commit","key":"https://github.com/docker/setup-buildx-action.git:v4.1.0","version":"d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5"}
{"name":"gha-uses-commit","key":"https://github.com/regclient/actions.git:main","version":"c70ad64367908075211b10dcd2ab9fad4bfa1816"}
{"name":"gha-uses-commit","key":"https://github.com/sigstore/cosign-installer.git:v4.1.2","version":"6f9f17788090df1f26f669e9d70d6ae9567deba6"}
{"name":"gha-uses-commit","key":"https://github.com/softprops/action-gh-release.git:v3.0.0","version":"b4309332981a82ec1c5618f44dd2e27cc8bfbfda"}
{"name":"gha-uses-semver","key":"https://github.com/actions/checkout.git","version":"v6.0.2"}
{"name":"gha-uses-semver","key":"https://github.com/actions/setup-go.git","version":"v6.4.0"}
{"name":"gha-uses-semver","key":"https://github.com/actions/stale.git","version":"v10.3.0"}
{"name":"gha-uses-semver","key":"https://github.com/actions/upload-artifact.git","version":"v7.0.1"}
{"name":"gha-uses-semver","key":"https://github.com/anchore/sbom-action.git","version":"v0.24.0"}
{"name":"gha-uses-semver","key":"https://github.com/docker/build-push-action.git","version":"v7.2.0"}
{"name":"gha-uses-semver","key":"https://github.com/docker/login-action.git","version":"v4.2.0"}
{"name":"gha-uses-semver","key":"https://github.com/docker/setup-buildx-action.git","version":"v4.1.0"}
{"name":"gha-uses-semver","key":"https://github.com/sigstore/cosign-installer.git","version":"v4.1.2"}
{"name":"gha-uses-semver","key":"https://github.com/softprops/action-gh-release.git","version":"v3.0.0"}
{"name":"go-mod-golang-release","key":"golang-oldest","version":"1.25.0"}
{"name":"makefile-ci-distribution","key":"docker.io/library/registry","version":"3.1.1"}
{"name":"makefile-ci-zot","key":"ghcr.io/project-zot/zot-linux-amd64","version":"v2.1.17"}
{"name":"makefile-go-vulncheck","key":"https://go.googlesource.com/vuln.git","version":"v1.3.0"}
{"name":"makefile-gofumpt","key":"https://github.com/mvdan/gofumpt.git","version":"v0.10.0"}
{"name":"makefile-gomajor","key":"https://github.com/icholy/gomajor.git","version":"v0.15.0"}
{"name":"makefile-gosec","key":"https://github.com/securego/gosec.git","version":"v2.26.1"}
{"name":"makefile-markdown-lint","key":"docker.io/davidanson/markdownlint-cli2","version":"v0.22.1"}
{"name":"makefile-osv-scanner","key":"https://github.com/google/osv-scanner.git","version":"v2.3.8"}
{"name":"makefile-staticcheck","key":"https://github.com/dominikh/go-tools.git","version":"v0.7.0"}
{"name":"makefile-syft-container-digest","key":"anchore/syft:v1.44.0","version":"sha256:86fde6445b483d902fe011dd9f68c4987dd94e07da1e9edc004e3c2422650de6"}
{"name":"makefile-syft-container-tag","key":"anchore/syft","version":"v1.44.0"}
{"name":"makefile-syft-version","key":"docker.io/anchore/syft","version":"v1.44.0"}
{"name":"osv-golang-release","key":"docker.io/library/golang","version":"1.26.3"}
{"name":"shell-alpine-digest","key":"docker.io/library/alpine:3.23.4","version":"sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11"}
{"name":"shell-alpine-tag-base","key":"docker.io/library/alpine","version":"3"}
{"name":"shell-alpine-tag-comment","key":"docker.io/library/alpine","version":"3.23.4"}
+346
View File
@@ -0,0 +1,346 @@
files:
"build/Dockerfile*":
processors:
- docker-arg-alpine-tag
- docker-arg-alpine-digest
- docker-arg-go-tag
- docker-arg-go-digest
- docker-arg-ecr
- docker-arg-gcr
- docker-arg-lunajson
- docker-arg-semver
"build/oci-image.sh":
processors:
- shell-alpine-tag-base
- shell-alpine-tag-comment
- shell-alpine-digest
".github/workflows/*.yml":
processors:
- gha-golang-matrix
- gha-golang-release
- gha-uses-vx
- gha-uses-semver
- gha-uses-commit
- gha-syft-version
- gha-cosign-version
- gha-alpine-tag-base
- gha-alpine-tag-comment
- gha-alpine-digest
"Makefile":
processors:
- makefile-gofumpt
- makefile-gomajor
- makefile-go-vulncheck
- makefile-markdown-lint
- makefile-gosec
- makefile-osv-scanner
- makefile-staticcheck
- makefile-syft-version
- makefile-syft-container-tag
- makefile-syft-container-digest
- makefile-ci-distribution
- makefile-ci-zot
"go.mod":
processors:
- go-mod-golang-release
".osv-scanner.toml":
processors:
- osv-golang-release
x-processor-tmpl:
git-commit: &git-commit
key: "{{ .SourceArgs.url }}:{{ .SourceArgs.ref }}"
scan: "regexp"
source: "git-commit"
filter:
expr: "^{{ .SourceArgs.ref }}$"
git-tag-semver: &git-tag-semver
key: "{{ .SourceArgs.url }}"
scan: "regexp"
source: "git-tag"
filter:
expr: '^v?\d+\.\d+\.\d+$'
sort:
method: "semver"
registry-digest: &registry-digest
key: "{{ .SourceArgs.image }}"
scan: "regexp"
source: "registry-digest"
registry-tag-semver: &registry-tag-semver
key: "{{ .SourceArgs.repo }}"
scan: "regexp"
source: "registry-tag"
filter:
expr: '^v?\d+\.\d+\.\d+$'
sort:
method: "semver"
processors:
docker-arg-alpine-tag:
<<: *registry-tag-semver
scanArgs:
regexp: '^ARG ALPINE_VER=(?P<Version>v?\d+\.\d+\.\d+)@(?P<SHA>sha256:[0-9a-f]+)\s*$'
sourceArgs:
repo: "docker.io/library/alpine"
docker-arg-alpine-digest:
<<: *registry-digest
scanArgs:
regexp: '^ARG ALPINE_VER=(?P<Tag>v?\d+\.\d+\.\d+)@(?P<Version>sha256:[0-9a-f]+)\s*$'
sourceArgs:
image: "docker.io/library/alpine:{{.ScanMatch.Tag}}"
docker-arg-go-tag:
<<: *registry-tag-semver
scanArgs:
regexp: '^ARG GO_VER=(?P<Version>[a-z0-9\-\.]+)-alpine@(?P<SHA>sha256:[0-9a-f]+)\s*$'
sourceArgs:
repo: "docker.io/library/golang"
docker-arg-go-digest:
<<: *registry-digest
scanArgs:
regexp: '^ARG GO_VER=(?P<Tag>[a-z0-9\-\.]+)@(?P<Version>sha256:[0-9a-f]+)\s*$'
sourceArgs:
image: "docker.io/library/golang:{{.ScanMatch.Tag}}"
docker-arg-ecr:
<<: *git-tag-semver
scanArgs:
regexp: '^ARG ECR_HELPER_VER=(?P<Version>v?\d+\.\d+\.\d+)\s*$'
sourceArgs:
url: "https://github.com/awslabs/amazon-ecr-credential-helper.git"
# get the version for the ecr-login nested package in the repo
filter:
expr: '^ecr-login/v?\d+\.\d+\.\d+$'
# sort and output only the version number without the package name prefix
sort:
method: "semver"
template: '{{ index (split . "/") 1 }}'
template: '{{ index (split .Version "/") 1 }}'
docker-arg-gcr:
<<: *git-tag-semver
scanArgs:
regexp: '^ARG GCR_HELPER_VER=(?P<Version>v?\d+\.\d+\.\d+)\s*$'
sourceArgs:
url: "https://github.com/GoogleCloudPlatform/docker-credential-gcr.git"
docker-arg-lunajson:
<<: *git-commit
scanArgs:
regexp: '^ARG LUNAJSON_COMMIT=(?P<Version>[0-9a-f]+)\s*$'
sourceArgs:
url: "https://github.com/grafi-tt/lunajson.git"
ref: master
docker-arg-semver:
<<: *git-commit
scanArgs:
regexp: '^ARG SEMVER_COMMIT=(?P<Version>[0-9a-f]+)\s*$'
sourceArgs:
url: "https://github.com/kikito/semver.lua.git"
ref: master
gha-alpine-digest:
<<: *registry-digest
scanArgs:
regexp: '^\s*ALPINE_DIGEST: "(?P<Version>sha256:[0-9a-f]+)"\s*#\s*(?P<Tag>\d+\.\d+\.\d+)\s*$'
sourceArgs:
image: "docker.io/library/alpine:{{ .ScanMatch.Tag }}"
gha-alpine-tag-base:
<<: *registry-tag-semver
scanArgs:
regexp: '^\s*ALPINE_NAME: "alpine:(?P<Version>v?\d+)"\s*$'
sourceArgs:
repo: "docker.io/library/alpine"
# only return the major version number in the tag to support detecting a change in the base image
template: '{{ index ( split .Version "." ) 0 }}'
gha-alpine-tag-comment:
<<: *registry-tag-semver
scanArgs:
regexp: '^\s*ALPINE_DIGEST: "(?P<Digest>sha256:[0-9a-f]+)"\s*#\s*(?P<Version>v?\d+\.\d+\.\d+)\s*$'
sourceArgs:
repo: "docker.io/library/alpine"
gha-cosign-version:
<<: *git-tag-semver
scanArgs:
regexp: '^\s*cosign-release: "(?P<Version>v?[0-9\.]+)"\s*$'
sourceArgs:
url: "https://github.com/sigstore/cosign.git"
filter:
expr: '^v?3\.\d+\.\d+$' # pin to v3, v4 will remove support for older clients
gha-golang-matrix:
<<: *registry-tag-semver
key: "golang-matrix"
scanArgs:
regexp: '^\s*gover: (?P<Version>\[["0-9, \.]+\])\s*$'
sourceArgs:
repo: "docker.io/library/golang"
filter:
expr: '^v?\d+\.\d+$'
template: '["{{ index .VerMap ( index .VerList 1 ) }}", "{{ index .VerMap ( index .VerList 0 ) }}"]'
gha-golang-release:
<<: *registry-tag-semver
key: "golang-latest"
scanArgs:
regexp: '^\s*RELEASE_GO_VER: "(?P<Version>v?[0-9\.]+)"\s*$'
sourceArgs:
repo: "docker.io/library/golang"
filter:
expr: '^v?\d+\.\d+$'
gha-syft-version:
<<: *registry-tag-semver
scanArgs:
regexp: '^\s*syft-version: "(?P<Version>v?[0-9\.]+)"\s*$'
sourceArgs:
repo: "docker.io/anchore/syft"
gha-uses-vx:
<<: *git-tag-semver
scanArgs:
regexp: '^\s+-?\s+uses: (?P<Repo>[^@/]+/[^@/]+)[^@]*@(?P<Commit>[0-9a-f]+)\s+#\s+(?P<Version>v?\d+)\s*$'
sourceArgs:
url: "https://github.com/{{ .ScanMatch.Repo }}.git"
filter:
expr: '^v?\d+$'
gha-uses-semver:
<<: *git-tag-semver
scanArgs:
regexp: '^\s+-?\s+uses: (?P<Repo>[^@/]+/[^@/]+)[^@]*@(?P<Commit>[0-9a-f]+)\s+#\s+(?P<Version>v?\d+\.\d+\.\d+)\s*$'
sourceArgs:
url: "https://github.com/{{ .ScanMatch.Repo }}.git"
gha-uses-commit:
<<: *git-commit
scanArgs:
regexp: '^\s+-?\s+uses: (?P<Repo>[^@/]+/[^@/]+)[^@]*@(?P<Version>[0-9a-f]+)\s+#\s+(?P<Ref>[\w\d\.]+)\s*$'
sourceArgs:
url: "https://github.com/{{ .ScanMatch.Repo }}.git"
ref: "{{ .ScanMatch.Ref }}"
go-mod-golang-release:
<<: *registry-tag-semver
key: "golang-oldest"
scanArgs:
regexp: '^go (?P<Version>[0-9\.]+)\s*$'
sourceArgs:
repo: "docker.io/library/golang"
filter:
expr: '^\d+\.\d+$'
template: '{{ index .VerMap ( index .VerList 1 ) }}.0'
makefile-ci-distribution:
<<: *registry-tag-semver
scanArgs:
regexp: '^CI_DISTRIBUTION_VER\?=(?P<Version>v?[0-9\.]+)\s*$'
sourceArgs:
repo: "docker.io/library/registry"
makefile-ci-zot:
<<: *registry-tag-semver
scanArgs:
regexp: '^CI_ZOT_VER\?=(?P<Version>v?[0-9\.]+)\s*$'
sourceArgs:
repo: "ghcr.io/project-zot/zot-linux-amd64"
makefile-gofumpt:
<<: *git-tag-semver
scanArgs:
regexp: '^GOFUMPT_VER\?=(?P<Version>v?[0-9\.]+)\s*$'
sourceArgs:
url: "https://github.com/mvdan/gofumpt.git"
makefile-gomajor:
<<: *git-tag-semver
scanArgs:
regexp: '^GOMAJOR_VER\?=(?P<Version>v?[0-9\.]+)\s*$'
sourceArgs:
url: "https://github.com/icholy/gomajor.git"
makefile-gosec:
<<: *git-tag-semver
scanArgs:
regexp: '^GOSEC_VER\?=(?P<Version>v?[0-9\.]+)\s*$'
sourceArgs:
url: "https://github.com/securego/gosec.git"
makefile-go-vulncheck:
<<: *git-tag-semver
scanArgs:
regexp: '^GO_VULNCHECK_VER\?=(?P<Version>v?[0-9\.]+)\s*$'
sourceArgs:
url: "https://go.googlesource.com/vuln.git"
makefile-markdown-lint:
<<: *registry-tag-semver
scanArgs:
regexp: '^MARKDOWN_LINT_VER\?=(?P<Version>v?[0-9\.]+)\s*$'
sourceArgs:
repo: "docker.io/davidanson/markdownlint-cli2"
makefile-osv-scanner:
<<: *git-tag-semver
scanArgs:
regexp: '^OSV_SCANNER_VER\?=(?P<Version>v?[0-9\.]+)\s*$'
sourceArgs:
url: "https://github.com/google/osv-scanner.git"
makefile-staticcheck:
<<: *git-tag-semver
scanArgs:
regexp: '^STATICCHECK_VER\?=(?P<Version>v?[0-9\.]+)\s*$'
sourceArgs:
url: "https://github.com/dominikh/go-tools.git"
filter:
# repo also has dated tags, ignore versions without a preceding "v"
expr: '^v\d+\.\d+\.\d+$'
makefile-syft-container-tag:
<<: *registry-tag-semver
scanArgs:
regexp: '^SYFT_CONTAINER\?=(?P<Repo>[^:]*):(?P<Version>v?[0-9\.]+)@(?P<Digest>sha256:[0-9a-f]+)\s*$'
sourceArgs:
repo: "{{ .ScanMatch.Repo }}"
makefile-syft-container-digest:
<<: *registry-digest
scanArgs:
regexp: '^SYFT_CONTAINER\?=(?P<Image>[^:]*):(?P<Tag>v?[0-9\.]+)@(?P<Version>sha256:[0-9a-f]+)\s*$'
sourceArgs:
image: "{{ .ScanMatch.Image }}:{{.ScanMatch.Tag}}"
makefile-syft-version:
<<: *registry-tag-semver
scanArgs:
regexp: '^SYFT_VERSION\?=(?P<Version>v[0-9\.]+)\s*$'
sourceArgs:
repo: "docker.io/anchore/syft"
osv-golang-release:
<<: *registry-tag-semver
scanArgs:
regexp: '^GoVersionOverride = "(?P<Version>v?[0-9\.]+)"\s*$'
sourceArgs:
repo: "docker.io/library/golang"
shell-alpine-tag-base:
<<: *registry-tag-semver
scanArgs:
regexp: '^\s*ALPINE_NAME="alpine:(?P<Version>v?\d+)"\s*$'
sourceArgs:
repo: "docker.io/library/alpine"
# only return the major version number in the tag to support detecting a change in the base image
template: '{{ index ( split .Version "." ) 0 }}'
shell-alpine-tag-comment:
<<: *registry-tag-semver
scanArgs:
regexp: '^\s*ALPINE_DIGEST="(?P<Digest>sha256:[0-9a-f]+)"\s*#\s*(?P<Version>v?\d+\.\d+\.\d+)\s*$'
sourceArgs:
repo: "docker.io/library/alpine"
shell-alpine-digest:
<<: *registry-digest
scanArgs:
regexp: '^\s*ALPINE_DIGEST="(?P<Version>sha256:[0-9a-f]+)"\s*#\s*(?P<Tag>\d+\.\d+\.\d+)\s*$'
sourceArgs:
image: "docker.io/library/alpine:{{ .ScanMatch.Tag }}"
scans:
regexp:
type: "regexp"
sources:
git-commit:
type: "git"
args:
type: "commit"
git-tag:
type: "git"
args:
type: "tag"
registry-digest:
type: "registry"
registry-tag:
type: "registry"
args:
type: "tag"
+134
View File
@@ -0,0 +1,134 @@
# Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or advances of
any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email address,
without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
<git@bmitch.net> or slack (I'm found on the CNCF, Docker, OCI, and OpenSSF
slacks).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
+80
View File
@@ -0,0 +1,80 @@
# Contributing
## Reporting security issues
Please see [SECURITY.md](security.md) for the process to report security issues.
## Reporting other issues
Please search for similar issues and if none are seen, report an issue at [github.com/regclient/regclient/issues](https://github.com/regclient/regclient/issues).
## Code style
This project attempts to follow these principles:
- Code is canonical Go, following styles and patterns commonly used by the Go community.
- Dependencies outside of the Go standard library should be minimized.
- Dependencies should be pinned to a specific digest and tracked by Go or version-check.
- Unit tests are strongly encouraged with a focus on test coverage of the successful path and common errors.
- Linters and other style formatting tools are used, please run `make all` before committing any changes.
## LLM Policy
This project expects all contributions to be developed by a human or created with a reproducible tool.
Developers using an AI/LLM tool to generate their contribution are expected to fully understand the entire contribution and the logic behind its design.
Contributions that appear to have been generated by an AI/LLM without a human review may result in a ban from future contributions to the project.
## Pull requests
PRs are welcome following the below guides:
- For anything beyond a minor fix, opening an issue is suggested to discuss possible solutions.
- Changes should be rebased on the main branch.
- Changes should be squashed to a single commit per logical change.
All changes must be signed (`git commit -s`) to indicate you agree to the [Developer Certificate or Origin](https://developercertificate.org/):
```text
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
The sign-off will include the following message in your commit:
```text
Signed-off-by: Your Name <your-email@example.org>
```
This needs to be your real name, no aliases please.
+191
View File
@@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2020 The regclient Authors.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+285
View File
@@ -0,0 +1,285 @@
COMMANDS?=regctl regsync regbot
BINARIES?=$(addprefix bin/,$(COMMANDS))
IMAGES?=$(addprefix docker-,$(COMMANDS))
ARTIFACT_PLATFORMS?=linux-amd64 linux-arm64 linux-ppc64le linux-s390x linux-riscv64 darwin-amd64 darwin-arm64 windows-amd64.exe freebsd-amd64
ARTIFACTS?=$(foreach cmd,$(addprefix artifacts/,$(COMMANDS)),$(addprefix $(cmd)-,$(ARTIFACT_PLATFORMS)))
IMAGE_PLATFORMS?=linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x,linux/riscv64
VCS_REPO?="https://github.com/regclient/regclient.git"
VCS_REF?=$(shell git rev-list -1 HEAD)
ifneq ($(shell git status --porcelain 2>/dev/null),)
VCS_REF := $(VCS_REF)-dirty
endif
VCS_VERSION?=$(shell vcs_describe="$$(git describe --all)"; \
vcs_version="(devel)"; \
if [ "$${vcs_describe}" != "$${vcs_describe#tags/}" ]; then \
vcs_version="$${vcs_describe#tags/}"; \
elif [ "$${vcs_describe}" != "$${vcs_describe#heads/}" ]; then \
vcs_version="$${vcs_describe#heads/}"; \
if [ "main" = "$${vcs_version}" ]; then vcs_version=edge; fi; \
fi; \
echo "$${vcs_version}" | sed -r 's#/+#-#g')
VCS_TAG?=$(shell git describe --tags --abbrev=0 2>/dev/null || true)
VCS_SEC?=$(shell git log -1 --format=%ct)
VCS_DATE?=$(shell date -d "@$(VCS_SEC)" +%Y-%m-%dT%H:%M:%SZ --utc)
LD_FLAGS?=-s -w -extldflags -static -buildid= -X \"github.com/regclient/regclient/internal/version.vcsTag=$(VCS_TAG)\"
GO_BUILD_FLAGS?=-trimpath -ldflags "$(LD_FLAGS)"
DOCKERFILE_EXT?=$(shell if docker build --help 2>/dev/null | grep -q -- '--progress'; then echo ".buildkit"; fi)
DOCKER_ARGS?=--build-arg "VCS_REF=$(VCS_REF)" --build-arg "VCS_VERSION=$(VCS_VERSION)" --build-arg "SOURCE_DATE_EPOCH=$(VCS_SEC)" --build-arg "BUILD_DATE=$(VCS_DATE)"
GOPATH?=$(shell go env GOPATH)
PWD:=$(shell pwd)
VER_BUMP?=$(shell command -v version-bump 2>/dev/null)
VER_BUMP_CONTAINER?=sudobmitch/version-bump:edge
ifeq "$(strip $(VER_BUMP))" ''
VER_BUMP=docker run --rm \
-v "$(shell pwd)/:$(shell pwd)/" -w "$(shell pwd)" \
-u "$(shell id -u):$(shell id -g)" \
$(VER_BUMP_CONTAINER)
endif
MARKDOWN_LINT_VER?=v0.22.1
GOFUMPT_VER?=v0.10.0
GOMAJOR_VER?=v0.15.0
GOSEC_VER?=v2.26.1
GO_VULNCHECK_VER?=v1.3.0
OSV_SCANNER_VER?=v2.3.8
SYFT?=$(shell command -v syft 2>/dev/null)
SYFT_CMD_VER:=$(shell [ -x "$(SYFT)" ] && echo "v$$($(SYFT) version | awk '/^Version: / {print $$2}')" || echo "0")
SYFT_VERSION?=v1.44.0
SYFT_CONTAINER?=anchore/syft:v1.44.0@sha256:86fde6445b483d902fe011dd9f68c4987dd94e07da1e9edc004e3c2422650de6
ifneq "$(SYFT_CMD_VER)" "$(SYFT_VERSION)"
SYFT=docker run --rm \
-v "$(shell pwd)/:$(shell pwd)/" -w "$(shell pwd)" \
-u "$(shell id -u):$(shell id -g)" \
$(SYFT_CONTAINER)
endif
STATICCHECK_VER?=v0.7.0
CI_DISTRIBUTION_VER?=3.1.1
CI_ZOT_VER?=v2.1.17
.PHONY: .FORCE
.FORCE:
.PHONY: all
all: fmt gofumpt gofix goimports vet test lint binaries ## Full build of Go binaries (including fmt, vet, test, and lint)
.PHONY: fmt
fmt: ## go fmt
go fmt ./...
.PHONY: gofumpt
gofumpt: $(GOPATH)/bin/gofumpt ## gofumpt is a stricter alternative to go fmt
gofumpt -l -w .
.PHONY: gofix
gofix: ## go fix
go fix ./...
goimports: $(GOPATH)/bin/goimports
$(GOPATH)/bin/goimports -w -format-only -local github.com/regclient .
.PHONY: vet
vet: ## go vet
go vet ./...
.PHONY: test
test: ## go test
go test -cover -race ./...
.PHONY: lint
lint: lint-go lint-goimports lint-md lint-gosec ## Run all linting
.PHONY: lint-go
lint-go: $(GOPATH)/bin/gofumpt $(GOPATH)/bin/staticcheck .FORCE ## Run linting for Go
$(GOPATH)/bin/staticcheck -checks all ./...
$(GOPATH)/bin/gofumpt -l -d .
errors=$$(go fix -diff ./...); if [ "$${errors}" != "" ]; then echo "$${errors}"; exit 1; fi
lint-goimports: $(GOPATH)/bin/goimports
@if [ -n "$$($(GOPATH)/bin/goimports -l -format-only -local github.com/regclient .)" ]; then \
echo $(GOPATH)/bin/goimports -d -format-only -local github.com/regclient .; \
$(GOPATH)/bin/goimports -d -format-only -local github.com/regclient .; \
exit 1; \
fi
# excluding types/platform pending resultion to https://github.com/securego/gosec/issues/1116
.PHONY: lint-gosec
lint-gosec: $(GOPATH)/bin/gosec .FORCE ## Run gosec
$(GOPATH)/bin/gosec -terse -exclude-dir types/platform ./...
.PHONY: lint-md
lint-md: .FORCE ## Run linting for markdown
docker run --rm -v "$(PWD):/workdir:ro" davidanson/markdownlint-cli2:$(MARKDOWN_LINT_VER) \
"**/*.md" "#vendor"
.PHONY: vulnerability-scan
vulnerability-scan: osv-scanner vulncheck-go ## Run all vulnerability scanners
.PHONY: osv-scanner
osv-scanner: $(GOPATH)/bin/osv-scanner .FORCE ## Run OSV Scanner
$(GOPATH)/bin/osv-scanner scan --config .osv-scanner.toml -r --licenses="Apache-2.0,BSD-3-Clause,MIT,CC-BY-SA-4.0,UNKNOWN" .
.PHONY: vulncheck-go
vulncheck-go: $(GOPATH)/bin/govulncheck .FORCE ## Run govulncheck
$(GOPATH)/bin/govulncheck ./...
.PHONY: vendor
vendor: ## Vendor Go modules
go mod vendor
.PHONY: binaries
binaries: $(BINARIES) ## Build Go binaries
bin/%: .FORCE
CGO_ENABLED=0 go build ${GO_BUILD_FLAGS} -o bin/$* ./cmd/$*
.PHONY: docker
docker: $(IMAGES) ## Build Docker images
docker-%: .FORCE
docker build -t regclient/$* -f build/Dockerfile.$*$(DOCKERFILE_EXT) $(DOCKER_ARGS) .
docker build -t regclient/$*:alpine -f build/Dockerfile.$*$(DOCKERFILE_EXT) --target release-alpine $(DOCKER_ARGS) .
.PHONY: oci-image
oci-image: $(addprefix oci-image-,$(COMMANDS)) ## Build reproducible images to an OCI Layout
oci-image-%: bin/regctl .FORCE
PATH="$(PWD)/bin:$(PATH)" build/oci-image.sh -r scratch -i "$*" -p "$(IMAGE_PLATFORMS)"
PATH="$(PWD)/bin:$(PATH)" build/oci-image.sh -r alpine -i "$*" -p "$(IMAGE_PLATFORMS)" -b "alpine:3"
.PHONY: test-docker
test-docker: $(addprefix test-docker-,$(COMMANDS)) ## Build multi-platform docker images (but do not tag)
test-docker-%:
docker buildx build --platform="$(IMAGE_PLATFORMS)" -f build/Dockerfile.$*.buildkit .
docker buildx build --platform="$(IMAGE_PLATFORMS)" -f build/Dockerfile.$*.buildkit --target release-alpine .
.PHONY: ci
ci: ci-distribution ci-zot ## Run CI tests against self hosted registries
.PHONY: ci-distribution
ci-distribution:
docker run --rm -d -p 5000 \
--label regclient-ci=true --name regclient-ci-distribution \
-e "REGISTRY_STORAGE_DELETE_ENABLED=true" \
docker.io/library/registry:$(CI_DISTRIBUTION_VER)
./build/ci-test.sh -t localhost:$$(docker port regclient-ci-distribution 5000 | head -1 | cut -f2 -d:)/test-ci
docker stop regclient-ci-distribution
.PHONY: ci-zot
ci-zot:
docker run --rm -d -p 5000 \
--label regclient-ci=true --name regclient-ci-zot \
-v "$$(pwd)/build/zot-config.json:/etc/zot/config.json:ro" \
ghcr.io/project-zot/zot-linux-amd64:$(CI_ZOT_VER)
./build/ci-test.sh -t localhost:$$(docker port regclient-ci-zot 5000 | head -1 | cut -f2 -d:)/test-ci
docker stop regclient-ci-zot
.PHONY: artifacts
artifacts: $(ARTIFACTS) ## Generate artifacts
.PHONY: artifact-pre
artifact-pre:
mkdir -p artifacts
artifacts/%: artifact-pre .FORCE
@set -e; \
target="$*"; \
command="$${target%%-*}"; \
platform_ext="$${target#*-}"; \
platform="$${platform_ext%.*}"; \
export GOOS="$${platform%%-*}"; \
export GOARCH="$${platform#*-}"; \
echo export GOOS=$${GOOS}; \
echo export GOARCH=$${GOARCH}; \
echo go build ${GO_BUILD_FLAGS} -o "$@" ./cmd/$${command}/; \
CGO_ENABLED=0 go build ${GO_BUILD_FLAGS} -o "$@" ./cmd/$${command}/; \
$(SYFT) scan -q "file:$@" --source-name "$${command}" -o cyclonedx-json >"artifacts/$${command}-$${platform}.cyclonedx.json"; \
$(SYFT) scan -q "file:$@" --source-name "$${command}" -o spdx-json >"artifacts/$${command}-$${platform}.spdx.json"
.PHONY: clean
clean: ## delete generated content
[ ! -d artifacts ] || rm -r artifacts
[ ! -d bin ] || rm -r bin
[ ! -d output ] || rm -r output
[ ! -d vendor ] || rm -r vendor
.PHONY: plugin-user
plugin-user:
mkdir -p ${HOME}/.docker/cli-plugins/
cp docker-plugin/docker-regclient ${HOME}/.docker/cli-plugins/docker-regctl
.PHONY: plugin-host
plugin-host:
sudo cp docker-plugin/docker-regclient /usr/libexec/docker/cli-plugins/docker-regctl
.PHONY: util-golang-major
util-golang-major: $(GOPATH)/bin/gomajor ## check for major dependency updates
$(GOPATH)/bin/gomajor list
.PHONY: util-golang-update
util-golang-update: ## update go module versions
go get -u -t ./...
go mod tidy
[ ! -d vendor ] || go mod vendor
.PHONY: util-release-preview
util-release-preview: $(GOPATH)/bin/gorelease ## preview changes for next release
git checkout main
./.github/release.sh -d
gorelease
.PHONY: util-release-run
util-release-run: ## generate a new release
git checkout main
./.github/release.sh
.PHONY: util-version-check
util-version-check: ## check all dependencies for updates
$(VER_BUMP) check
.PHONY: util-version-update
util-version-update: ## update versions on all dependencies
$(VER_BUMP) update
$(GOPATH)/bin/gofumpt: .FORCE
@[ -f "$(GOPATH)/bin/gofumpt" ] \
&& [ "$$($(GOPATH)/bin/gofumpt -version | cut -f 1 -d ' ')" = "$(GOFUMPT_VER)" ] \
|| go install mvdan.cc/gofumpt@$(GOFUMPT_VER)
$(GOPATH)/bin/gomajor: .FORCE
@[ -f "$(GOPATH)/bin/gomajor" ] \
&& [ "$$($(GOPATH)/bin/gomajor version | grep '^version' | cut -f 2 -d ' ')" = "$(GOMAJOR_VER)" ] \
|| go install github.com/icholy/gomajor@$(GOMAJOR_VER)
$(GOPATH)/bin/goimports: .FORCE
@[ -f "$(GOPATH)/bin/goimports" ] && [ "$$(go version | cut -f3 -d' ')" = "$$(go version $(GOPATH)/bin/goimports | cut -f2 -d' ')" ] \
|| go install golang.org/x/tools/cmd/goimports@latest
$(GOPATH)/bin/gorelease: .FORCE
@[ -f "$(GOPATH)/bin/gorelease" ] && [ "$$(go version | cut -f3 -d' ')" = "$$(go version $(GOPATH)/bin/gorelease | cut -f2 -d' ')" ] \
|| go install golang.org/x/exp/cmd/gorelease@latest
$(GOPATH)/bin/gosec: .FORCE
@[ -f $(GOPATH)/bin/gosec ] \
&& [ "$$($(GOPATH)/bin/gosec -version | grep '^Version' | cut -f 2 -d ' ')" = "$(GOSEC_VER)" ] \
|| go install -ldflags '-X main.Version=$(GOSEC_VER) -X main.GitTag=$(GOSEC_VER)' \
github.com/securego/gosec/v2/cmd/gosec@$(GOSEC_VER)
$(GOPATH)/bin/staticcheck: .FORCE
@[ -f $(GOPATH)/bin/staticcheck ] \
&& [ "$$($(GOPATH)/bin/staticcheck -version | cut -f 3 -d ' ' | tr -d '()')" = "$(STATICCHECK_VER)" ] \
|| go install "honnef.co/go/tools/cmd/staticcheck@$(STATICCHECK_VER)"
$(GOPATH)/bin/govulncheck: .FORCE
@[ -f $(GOPATH)/bin/govulncheck ] \
&& [ $$(go version -m $(GOPATH)/bin/govulncheck | \
awk -F ' ' '{ if ($$1 == "mod" && $$2 == "golang.org/x/vuln") { printf "%s\n", $$3 } }') = "$(GO_VULNCHECK_VER)" ] \
|| CGO_ENABLED=0 go install "golang.org/x/vuln/cmd/govulncheck@$(GO_VULNCHECK_VER)"
$(GOPATH)/bin/osv-scanner: .FORCE
@[ -f $(GOPATH)/bin/osv-scanner ] \
&& [ "$$(osv-scanner --version | awk -F ': ' '{ if ($$1 == "osv-scanner version") { printf "%s\n", $$2 } }')" = "$(OSV_SCANNER_VER)" ] \
|| CGO_ENABLED=0 go install "github.com/google/osv-scanner/v2/cmd/osv-scanner@$(OSV_SCANNER_VER)"
.PHONY: help
help: # Display help
@awk -F ':|##' '/^[^\t].+?:.*?##/ { printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF }' $(MAKEFILE_LIST)

Some files were not shown because too many files have changed in this diff Show More