From 41c8aa2571f2310df5535c6d5b3eacb2f66242ee Mon Sep 17 00:00:00 2001 From: mailaenderli Date: Tue, 21 Apr 2026 10:26:48 +0000 Subject: [PATCH 1/3] fix: update go feature version to 1.3.3 --- .devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4d528bb..db73728 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,7 +13,7 @@ "GOMODCACHE": "${containerWorkspaceFolder}/.go_cache" }, "features": { - "ghcr.io/devcontainers/features/go:1.3.2": { + "ghcr.io/devcontainers/features/go:1.3.3": { "version": "1.24.7" }, "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}, From 523dcd679b9749c02607f469721e2b2182a2db37 Mon Sep 17 00:00:00 2001 From: mailaenderli Date: Tue, 21 Apr 2026 10:29:06 +0000 Subject: [PATCH 2/3] ci: fix devcontainer-collection.json by publishing features together --- .github/actions/setup-env/action.yml | 22 ++++++++++++++ .github/workflows/ci.yml | 39 ++++++++++++++++--------- build/build.go | 43 ++++++++-------------------- 3 files changed, 59 insertions(+), 45 deletions(-) create mode 100644 .github/actions/setup-env/action.yml diff --git a/.github/actions/setup-env/action.yml b/.github/actions/setup-env/action.yml new file mode 100644 index 0000000..55c14bd --- /dev/null +++ b/.github/actions/setup-env/action.yml @@ -0,0 +1,22 @@ +name: Setup Build Environment +description: Setup Go, Node, and DevContainer CLI + +inputs: + go-version: + required: true + description: Go version to setup + node-version: + required: true + description: Node version to setup + +runs: + using: composite + steps: + - uses: actions/setup-go@v5 + with: + go-version: ${{ inputs.go-version }} + - uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + - run: npm install -g @devcontainers/cli + shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69f56ef..abc5f05 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ on: env: GO_VERSION: 1.24.5 + NODE_VERSION: 22 jobs: # Build Job @@ -52,27 +53,37 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 + - name: Setup environment + uses: ./.github/actions/setup-env with: - go-version: "${{ env.GO_VERSION }}" - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 22 - - - name: Setup DevContainer-CLI - run: npm install -g @devcontainers/cli + go-version: ${{ env.GO_VERSION }} + node-version: ${{ env.NODE_VERSION }} - name: Build run: go run ./build --target "Feature:${{ matrix.feature }}:Package" - name: Test run: go run ./build --target "Feature:${{ matrix.feature }}:Test" - + + # Publish Job + publish: + needs: build + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup environment + uses: ./.github/actions/setup-env + with: + go-version: ${{ env.GO_VERSION }} + node-version: ${{ env.NODE_VERSION }} + - name: Publish - if: github.ref == 'refs/heads/main' - run: go run ./build --target "Feature:${{ matrix.feature }}:Publish" + run: go run ./build --target "Features:Publish" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/build/build.go b/build/build.go index 442f5d6..2c86da1 100644 --- a/build/build.go +++ b/build/build.go @@ -80,145 +80,120 @@ func init() { return os.WriteFile("README.md", []byte(strings.Join(outputLines, "\n")), os.ModePerm) }) + ////////// publish features + gotaskr.Task("Features:Publish", func() error { return publishFeatures() }) + ////////// browsers gotaskr.Task("Feature:browsers:Package", func() error { return packageFeature("browsers") }) gotaskr.Task("Feature:browsers:Test", func() error { return testFeature("browsers") }) - gotaskr.Task("Feature:browsers:Publish", func() error { return publishFeature("browsers") }) ////////// build-essential gotaskr.Task("Feature:build-essential:Package", func() error { return packageFeature("build-essential") }) gotaskr.Task("Feature:build-essential:Test", func() error { return testFeature("build-essential") }) - gotaskr.Task("Feature:build-essential:Publish", func() error { return publishFeature("build-essential") }) ////////// cypress-deps gotaskr.Task("Feature:cypress-deps:Package", func() error { return packageFeature("cypress-deps") }) gotaskr.Task("Feature:cypress-deps:Test", func() error { return testFeature("cypress-deps") }) - gotaskr.Task("Feature:cypress-deps:Publish", func() error { return publishFeature("cypress-deps") }) ////////// docker-out gotaskr.Task("Feature:docker-out:Package", func() error { return packageFeature("docker-out") }) gotaskr.Task("Feature:docker-out:Test", func() error { return testFeature("docker-out") }) - gotaskr.Task("Feature:docker-out:Publish", func() error { return publishFeature("docker-out") }) ////////// dotnet gotaskr.Task("Feature:dotnet:Package", func() error { return packageFeature("dotnet") }) gotaskr.Task("Feature:dotnet:Test", func() error { return testFeature("dotnet") }) - gotaskr.Task("Feature:dotnet:Publish", func() error { return publishFeature("dotnet") }) ////////// eclipse-deps gotaskr.Task("Feature:eclipse-deps:Package", func() error { return packageFeature("eclipse-deps") }) gotaskr.Task("Feature:eclipse-deps:Test", func() error { return testFeature("eclipse-deps") }) - gotaskr.Task("Feature:eclipse-deps:Publish", func() error { return publishFeature("eclipse-deps") }) ////////// git-lfs gotaskr.Task("Feature:git-lfs:Package", func() error { return packageFeature("git-lfs") }) gotaskr.Task("Feature:git-lfs:Test", func() error { return testFeature("git-lfs") }) - gotaskr.Task("Feature:git-lfs:Publish", func() error { return publishFeature("git-lfs") }) ////////// gitlab-cli gotaskr.Task("Feature:gitlab-cli:Package", func() error { return packageFeature("gitlab-cli") }) gotaskr.Task("Feature:gitlab-cli:Test", func() error { return testFeature("gitlab-cli") }) - gotaskr.Task("Feature:gitlab-cli:Publish", func() error { return publishFeature("gitlab-cli") }) ////////// go gotaskr.Task("Feature:go:Package", func() error { return packageFeature("go") }) gotaskr.Task("Feature:go:Test", func() error { return testFeature("go") }) - gotaskr.Task("Feature:go:Publish", func() error { return publishFeature("go") }) ////////// gonovate gotaskr.Task("Feature:gonovate:Package", func() error { return packageFeature("gonovate") }) gotaskr.Task("Feature:gonovate:Test", func() error { return testFeature("gonovate") }) - gotaskr.Task("Feature:gonovate:Publish", func() error { return publishFeature("gonovate") }) ////////// goreleaser gotaskr.Task("Feature:goreleaser:Package", func() error { return packageFeature("goreleaser") }) gotaskr.Task("Feature:goreleaser:Test", func() error { return testFeature("goreleaser") }) - gotaskr.Task("Feature:goreleaser:Publish", func() error { return publishFeature("goreleaser") }) ////////// instant-client gotaskr.Task("Feature:instant-client:Package", func() error { return packageFeature("instant-client") }) gotaskr.Task("Feature:instant-client:Test", func() error { return testFeature("instant-client") }) - gotaskr.Task("Feature:instant-client:Publish", func() error { return publishFeature("instant-client") }) ////////// jfrog-cli gotaskr.Task("Feature:jfrog-cli:Package", func() error { return packageFeature("jfrog-cli") }) gotaskr.Task("Feature:jfrog-cli:Test", func() error { return testFeature("jfrog-cli") }) - gotaskr.Task("Feature:jfrog-cli:Publish", func() error { return publishFeature("jfrog-cli") }) ////////// kubectl gotaskr.Task("Feature:kubectl:Package", func() error { return packageFeature("kubectl") }) gotaskr.Task("Feature:kubectl:Test", func() error { return testFeature("kubectl") }) - gotaskr.Task("Feature:kubectl:Publish", func() error { return publishFeature("kubectl") }) ////////// locale gotaskr.Task("Feature:locale:Package", func() error { return packageFeature("locale") }) gotaskr.Task("Feature:locale:Test", func() error { return testFeature("locale") }) - gotaskr.Task("Feature:locale:Publish", func() error { return publishFeature("locale") }) ////////// make gotaskr.Task("Feature:make:Package", func() error { return packageFeature("make") }) gotaskr.Task("Feature:make:Test", func() error { return testFeature("make") }) - gotaskr.Task("Feature:make:Publish", func() error { return publishFeature("make") }) ////////// mingw gotaskr.Task("Feature:mingw:Package", func() error { return packageFeature("mingw") }) gotaskr.Task("Feature:mingw:Test", func() error { return testFeature("mingw") }) - gotaskr.Task("Feature:mingw:Publish", func() error { return publishFeature("mingw") }) ////////// nginx gotaskr.Task("Feature:nginx:Package", func() error { return packageFeature("nginx") }) gotaskr.Task("Feature:nginx:Test", func() error { return testFeature("nginx") }) - gotaskr.Task("Feature:nginx:Publish", func() error { return publishFeature("nginx") }) ////////// node gotaskr.Task("Feature:node:Package", func() error { return packageFeature("node") }) gotaskr.Task("Feature:node:Test", func() error { return testFeature("node") }) - gotaskr.Task("Feature:node:Publish", func() error { return publishFeature("node") }) ////////// nvidia-cuda gotaskr.Task("Feature:nvidia-cuda:Package", func() error { return packageFeature("nvidia-cuda") }) gotaskr.Task("Feature:nvidia-cuda:Test", func() error { return testFeature("nvidia-cuda") }) - gotaskr.Task("Feature:nvidia-cuda:Publish", func() error { return publishFeature("nvidia-cuda") }) ////////// playwright-deps gotaskr.Task("Feature:playwright-deps:Package", func() error { return packageFeature("playwright-deps") }) gotaskr.Task("Feature:playwright-deps:Test", func() error { return testFeature("playwright-deps") }) - gotaskr.Task("Feature:playwright-deps:Publish", func() error { return publishFeature("playwright-deps") }) ////////// python gotaskr.Task("Feature:python:Package", func() error { return packageFeature("python") }) gotaskr.Task("Feature:python:Test", func() error { return testFeature("python") }) - gotaskr.Task("Feature:python:Publish", func() error { return publishFeature("python") }) ////////// rust gotaskr.Task("Feature:rust:Package", func() error { return packageFeature("rust") }) gotaskr.Task("Feature:rust:Test", func() error { return testFeature("rust") }) - gotaskr.Task("Feature:rust:Publish", func() error { return publishFeature("rust") }) ////////// sonar-scanner-cli gotaskr.Task("Feature:sonar-scanner-cli:Package", func() error { return packageFeature("sonar-scanner-cli") }) gotaskr.Task("Feature:sonar-scanner-cli:Test", func() error { return testFeature("sonar-scanner-cli") }) - gotaskr.Task("Feature:sonar-scanner-cli:Publish", func() error { return publishFeature("sonar-scanner-cli") }) ////////// system-packages gotaskr.Task("Feature:system-packages:Package", func() error { return packageFeature("system-packages") }) gotaskr.Task("Feature:system-packages:Test", func() error { return testFeature("system-packages") }) - gotaskr.Task("Feature:system-packages:Publish", func() error { return publishFeature("system-packages") }) ////////// timezone gotaskr.Task("Feature:timezone:Package", func() error { return packageFeature("timezone") }) gotaskr.Task("Feature:timezone:Test", func() error { return testFeature("timezone") }) - gotaskr.Task("Feature:timezone:Publish", func() error { return publishFeature("timezone") }) ////////// vault-cli gotaskr.Task("Feature:vault-cli:Package", func() error { return packageFeature("vault-cli") }) gotaskr.Task("Feature:vault-cli:Test", func() error { return testFeature("vault-cli") }) - gotaskr.Task("Feature:vault-cli:Publish", func() error { return publishFeature("vault-cli") }) ////////// zig gotaskr.Task("Feature:zig:Package", func() error { return packageFeature("zig") }) gotaskr.Task("Feature:zig:Test", func() error { return testFeature("zig") }) - gotaskr.Task("Feature:zig:Publish", func() error { return publishFeature("zig") }) } //////////////////////////////////////////////////////////// @@ -439,15 +414,21 @@ func testFeature(featureName string) error { }*/ } -func publishFeature(featureName string) error { +func publishFeatures() error { registry := "ghcr.io" namespace := "postfinance/devcontainer-features" - tempDir := ".prepared-feature" + tempDir := ".prepared-features" defer os.RemoveAll(tempDir) - if err := prepareFeature(featureName, tempDir); err != nil { + featureList, err := getFeatures() + if err != nil { return err } + for _, feature := range featureList { + if err := prepareFeature(feature, path.Join(tempDir, feature)); err != nil { + return err + } + } // No authentication needed - DevContainerCLI supports GITHUB_TOKEN // os.Setenv("DEVCONTAINERS_OCI_AUTH", "ghcr.io|USERNAME|"+os.Getenv("GITHUB_TOKEN")) From 16ed05536cc22b6f5a71f1c4f6dfc21e3e592da6 Mon Sep 17 00:00:00 2001 From: mailaenderli Date: Tue, 21 Apr 2026 12:41:53 +0000 Subject: [PATCH 3/3] fix: ensure temporary directory is clean before publishing features --- build/build.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/build.go b/build/build.go index 2c86da1..d7c7ae4 100644 --- a/build/build.go +++ b/build/build.go @@ -419,6 +419,8 @@ func publishFeatures() error { namespace := "postfinance/devcontainer-features" tempDir := ".prepared-features" + // Ensure the temp dir is clean before preparing the features and removed after publishing + os.RemoveAll(tempDir) defer os.RemoveAll(tempDir) featureList, err := getFeatures() if err != nil {