diff --git a/.github/workflows/release_publish.yml b/.github/workflows/release_publish.yml index eee42c028..5c51c69e6 100644 --- a/.github/workflows/release_publish.yml +++ b/.github/workflows/release_publish.yml @@ -101,6 +101,17 @@ jobs: with: ref: ${{ inputs.ref }} persist-credentials: false + - name: Read the pinned OpenObservation revision + id: observation + run: | + revision=$(bash Scripts/CI/get_revision.sh openobservation) + echo "revision=$revision" >> "$GITHUB_OUTPUT" + - uses: actions/checkout@v4 + with: + repository: OpenSwiftUIProject/OpenObservation + ref: ${{ steps.observation.outputs.revision }} + path: observation-repo + persist-credentials: false - uses: actions/checkout@v4 with: repository: OpenSwiftUIProject/OpenSwiftUI-spm @@ -112,4 +123,4 @@ jobs: env: RELEASE_MANIFEST: ${{ needs.assets.outputs.manifest }} VERSION: ${{ inputs.version }} - run: bash Scripts/CI/update_binary_package.sh "$PWD" "$PWD/binary-repo" + run: bash Scripts/CI/update_binary_package.sh "$PWD" "$PWD/binary-repo" "$PWD/observation-repo" diff --git a/Scripts/CI/Tests/binary-package-tests.js b/Scripts/CI/Tests/binary-package-tests.js index 24ec95f81..c29202dbc 100644 --- a/Scripts/CI/Tests/binary-package-tests.js +++ b/Scripts/CI/Tests/binary-package-tests.js @@ -20,12 +20,15 @@ async function fixture(t) { const root = await fs.mkdtemp(path.join(os.tmpdir(), "openswiftui-binary-release-")); t.after(() => fs.rm(root, { recursive: true, force: true })); const source = path.join(root, "source"); + const observation = path.join(root, "observation"); const remote = path.join(root, "remote.git"); const binary = path.join(root, "binary"); await fs.mkdir(path.join(source, "Scripts/CI"), { recursive: true }); await fs.mkdir(path.join(source, "Sources/OpenSwiftUIMacros"), { recursive: true }); await fs.copyFile(path.join(__dirname, "../release.js"), path.join(source, "Scripts/CI/release.js")); await fs.writeFile(path.join(source, "Sources/OpenSwiftUIMacros/Macro.swift"), "// Current macros\n"); + await fs.mkdir(path.join(observation, "Sources/OpenObservationMacros"), { recursive: true }); + await fs.writeFile(path.join(observation, "Sources/OpenObservationMacros/Macro.swift"), "// Observation macros\n"); await fs.mkdir(remote); git(remote, "init", "--bare", "--initial-branch=main"); git(root, "clone", remote, binary); @@ -41,14 +44,32 @@ async function fixture(t) { files: Object.fromEntries(frameworks.map(name => [`${name}.xcframework.zip`, "b".repeat(64)])), }; const run = (overrides = {}) => execFileSync("bash", [ - path.join(__dirname, "../update_binary_package.sh"), source, binary, + path.join(__dirname, "../update_binary_package.sh"), source, binary, observation, ], { env: { ...gitEnvironment, VERSION: version, RELEASE_MANIFEST: JSON.stringify(manifest), ...overrides }, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], }); - return { root, source, remote, binary, manifest, run }; + return { root, source, observation, remote, binary, manifest, run }; } +test("binary package publication replaces observation macros and detects mismatched retries", async t => { + const { observation, remote, binary, run } = await fixture(t); + const macros = "Sources/OpenObservationMacros"; + await fs.mkdir(path.join(binary, macros), { recursive: true }); + await fs.writeFile(path.join(binary, macros, "Obsolete.swift"), "// Removed macro\n"); + git(binary, "add", "."); + git(binary, "commit", "-m", "Add an obsolete macro"); + run(); + const published = git(remote, "rev-parse", `refs/tags/${version}`); + assert.equal(git(remote, "show", `${version}:${macros}/Macro.swift`), "// Observation macros"); + assert.throws(() => git(remote, "show", `${version}:${macros}/Obsolete.swift`)); + run(); + assert.equal(git(remote, "rev-parse", "main"), published); + await fs.appendFile(path.join(observation, macros, "Macro.swift"), "// Changed\n"); + assert.throws(run, /different artifacts or macros/); + assert.equal(git(remote, "rev-parse", `refs/tags/${version}`), published); +}); + test("binary package publication creates matching refs and retries without another commit", async t => { const { source, remote, binary, run } = await fixture(t); run(); diff --git a/Scripts/CI/release.js b/Scripts/CI/release.js index 0d87de202..c25f7a07a 100644 --- a/Scripts/CI/release.js +++ b/Scripts/CI/release.js @@ -177,7 +177,7 @@ async function publishRelease({ github, repo, plan }) { } } -async function renderBinaryPackage(source, binary, manifest) { +async function renderBinaryPackage(source, binary, observation, manifest) { validateManifest(manifest, manifest.version, manifest.sha); const template = await fs.readFile(path.join(binary, "Package.swift.template"), "utf8"); const values = { VERSION: manifest.version }; @@ -193,9 +193,11 @@ async function renderBinaryPackage(source, binary, manifest) { /(\.package\(url:\s*"https:\/\/github\.com\/OpenSwiftUIProject\/OpenSwiftUI-spm",\s*from:\s*")[^"]+("\))/g, (_, prefix, suffix) => `${prefix}${manifest.version}${suffix}`, )); - const macros = path.join(binary, "Sources/OpenSwiftUIMacros"); - await fs.rm(macros, { recursive: true, force: true }); - await fs.cp(path.join(source, "Sources/OpenSwiftUIMacros"), macros, { recursive: true }); + for (const [name, repository] of [["OpenSwiftUIMacros", source], ["OpenObservationMacros", observation]]) { + const macros = path.join(binary, "Sources", name); + await fs.rm(macros, { recursive: true, force: true }); + await fs.cp(path.join(repository, "Sources", name), macros, { recursive: true }); + } } module.exports = { diff --git a/Scripts/CI/update_binary_package.sh b/Scripts/CI/update_binary_package.sh index 1f5224696..185cf6cf1 100644 --- a/Scripts/CI/update_binary_package.sh +++ b/Scripts/CI/update_binary_package.sh @@ -4,8 +4,10 @@ set -euo pipefail source_root="$1" binary_root="$2" +observation_root="$3" source_root="$(cd "$source_root" && pwd -P)" binary_root="$(cd "$binary_root" && pwd -P)" +observation_root="$(cd "$observation_root" && pwd -P)" node -e 'require(process.argv[1]).validateVersion(process.env.VERSION)' "$source_root/Scripts/CI/release.js" cd "$binary_root" @@ -18,8 +20,8 @@ if git show-ref --verify --quiet "refs/tags/$VERSION"; then git checkout --detach "$VERSION" fi -node "$source_root/Scripts/CI/release.js" render-package "$source_root" "$binary_root" -git add -A -- Package.swift README.md Sources/OpenSwiftUIMacros +node "$source_root/Scripts/CI/release.js" render-package "$source_root" "$binary_root" "$observation_root" +git add -A -- Package.swift README.md Sources/OpenSwiftUIMacros Sources/OpenObservationMacros if [[ "$tag_exists" == true ]]; then if ! git diff --cached --quiet; then